Port BBS' improved Mesaure and Assembly Gizmo (#7396)

This PR replaces the existing Measure gizmo with the improved version from BambuStudio, with the following noticable improvements:
- Can measure between multiple objects
- Can be used in assemly view

Also ported the Assembly Gizmo from BBS, which makes assemble much easier.

Huge thanks to BambuLab!
![image](https://github.com/user-attachments/assets/3d69e51c-029b-4cbe-b573-0f38bb02b6aa)
![image](https://github.com/user-attachments/assets/8353d643-4c29-493b-8384-2747879fa8c8)
This commit is contained in:
SoftFever 2024-12-02 22:04:01 +08:00 committed by GitHub
commit b331830604
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 2374 additions and 955 deletions

View file

@ -0,0 +1,7 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="7.80615" y="25.5195" width="29" height="9.19336" stroke="#009688"/>
<rect x="19.1368" y="5.45404" width="15" height="6.00417" transform="rotate(25.5934 19.1368 5.45404)" stroke="#2b3436"/>
<rect x="14.8062" y="18.5156" width="15" height="6.00417" stroke="#009688" stroke-dasharray="1 1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.4877 17.5905C13.5899 17.4883 13.5432 17.3137 13.4035 17.2762L4.93754 15.0078C4.79787 14.9704 4.67006 15.0982 4.70749 15.2378L6.97593 23.7038C7.01336 23.8435 7.18794 23.8902 7.29019 23.788L13.4877 17.5905ZM7.54629 22.2037L11.9034 17.8466L5.95148 16.2518L7.54629 22.2037Z" fill="#2b3436"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.22678 16.0611C9.70526 15.1933 10.3436 14.4213 11.1102 13.787C11.8768 13.1526 12.7546 12.6699 13.6966 12.3623C13.7768 12.3361 13.8575 12.3112 13.9386 12.2875C13.9519 12.2837 13.9648 12.2792 13.9775 12.2743C14.1795 12.1952 14.3022 11.9815 14.252 11.767C14.1986 11.539 13.9701 11.3964 13.745 11.4608C13.7304 11.4649 13.7158 11.4692 13.7013 11.4734C13.609 11.5003 13.5172 11.5287 13.426 11.5586C12.3857 11.8993 11.4164 12.4329 10.5696 13.1336C9.72279 13.8344 9.01727 14.6868 8.4879 15.6449C8.44147 15.7289 8.3964 15.8138 8.35271 15.8994C8.34582 15.9129 8.33897 15.9264 8.33215 15.94C8.22679 16.1491 8.3241 16.4002 8.53808 16.4953C8.73944 16.5848 8.97228 16.5042 9.08775 16.3206C9.09499 16.3091 9.10177 16.2972 9.10806 16.2848C9.14646 16.2096 9.18604 16.135 9.22678 16.0611Z" fill="#2b3436"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,7 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="7.80615" y="25.5195" width="29" height="9.19336" stroke="#009688"/>
<rect x="19.1368" y="5.45404" width="15" height="6.00417" transform="rotate(25.5934 19.1368 5.45404)" stroke="#b6b6b6"/>
<rect x="14.8062" y="18.5156" width="15" height="6.00417" stroke="#009688" stroke-dasharray="1 1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.4877 17.5905C13.5899 17.4883 13.5432 17.3137 13.4035 17.2762L4.93754 15.0078C4.79787 14.9704 4.67006 15.0982 4.70749 15.2378L6.97593 23.7038C7.01336 23.8435 7.18794 23.8902 7.29019 23.788L13.4877 17.5905ZM7.54629 22.2037L11.9034 17.8466L5.95148 16.2518L7.54629 22.2037Z" fill="#009688"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.22678 16.0611C9.70526 15.1933 10.3436 14.4213 11.1102 13.787C11.8768 13.1526 12.7546 12.6699 13.6966 12.3623C13.7768 12.3361 13.8575 12.3112 13.9386 12.2875C13.9519 12.2837 13.9648 12.2792 13.9775 12.2743C14.1795 12.1952 14.3022 11.9815 14.252 11.767C14.1986 11.539 13.9701 11.3964 13.745 11.4608C13.7304 11.4649 13.7158 11.4692 13.7013 11.4734C13.609 11.5003 13.5172 11.5287 13.426 11.5586C12.3857 11.8993 11.4164 12.4329 10.5696 13.1336C9.72279 13.8344 9.01727 14.6868 8.4879 15.6449C8.44147 15.7289 8.3964 15.8138 8.35271 15.8994C8.34582 15.9129 8.33897 15.9264 8.33215 15.94C8.22679 16.1491 8.3241 16.4002 8.53808 16.4953C8.73944 16.5848 8.97228 16.5042 9.08775 16.3206C9.09499 16.3091 9.10177 16.2972 9.10806 16.2848C9.14646 16.2096 9.18604 16.135 9.22678 16.0611Z" fill="#b6b6b6"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -67,6 +67,7 @@ public:
static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; }
static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; }
static const ColorRGB ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255}; }
static const ColorRGB WARNING() { return {241.0f / 255, 117.f / 255.0f, 78.0f / 255}; }
static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; }
static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; }

View file

@ -717,53 +717,6 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
return out;
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;
Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();
scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);
if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;
// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
// For parsing a transformation matrix from 3MF / AMF.
Transform3d transform3d_from_string(const std::string& transform_str)
{
@ -812,4 +765,78 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
return (axis.z() < 0) ? -angle : angle;
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;
Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();
scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);
if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;
// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
Transformation mat_around_a_point_rotate(const Transformation &InMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian)
{
auto xyz = InMat.get_offset();
Transformation left;
left.set_offset(-xyz); // at world origin
auto curMat = left * InMat;
auto qua = Eigen::Quaterniond(Eigen::AngleAxisd(rotate_theta_radian, axis));
qua.normalize();
Transform3d cur_matrix;
Transformation rotateMat4;
rotateMat4.set_matrix(cur_matrix.fromPositionOrientationScale(Vec3d(0., 0., 0.), qua, Vec3d(1., 1., 1.)));
curMat = rotateMat4 * curMat; // along_fix_axis
// rotate mat4 along fix pt
Transformation temp_world;
auto qua_world = Eigen::Quaterniond(Eigen::AngleAxisd(0, axis));
qua_world.normalize();
Transform3d cur_matrix_world;
temp_world.set_matrix(cur_matrix_world.fromPositionOrientationScale(pt, qua_world, Vec3d(1., 1., 1.)));
auto temp_xyz = temp_world.get_matrix().inverse() * xyz;
auto new_pos = temp_world.get_matrix() * (rotateMat4.get_matrix() * temp_xyz);
curMat.set_offset(new_pos);
return curMat;
}
}} // namespace Slic3r::Geometry

View file

@ -10,7 +10,7 @@
// Serialization through the Cereal library
#include <cereal/access.hpp>
namespace Slic3r {
namespace Slic3r {
namespace ClipperLib {
class PolyNode;
@ -544,6 +544,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
}
Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian);
} } // namespace Slicer::Geometry
#endif

View file

@ -13,7 +13,27 @@
namespace Slic3r {
namespace Measure {
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt)
{
auto normal = plane_normal.normalized();
auto BA = plane_origin - pt;
auto length = BA.dot(normal);
intersection_pt = pt + length * normal;
return true;
}
Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal)
{
Vec3d dir(1, 0, 0);
float eps = 1e-3;
if (abs(plane_normal.dot(dir)) > 1 - eps) {
dir = Vec3d(0, 1, 0);
}
auto new_pt = plane_origin + dir;
Vec3d retult;
get_point_projection_to_plane(new_pt, plane_origin, plane_normal, retult);
return retult;
}
constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it
@ -33,7 +53,7 @@ static std::tuple<Vec3d, double, double> get_center_and_radius(const std::vector
double error = std::numeric_limits<double>::max();
auto circle = Geometry::circle_ransac(out, iter, &error);
return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error);
}
@ -69,16 +89,18 @@ public:
bool features_extracted = false;
};
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point);
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane);
int get_num_of_planes() const;
const std::vector<int>& get_plane_triangle_indices(int idx) const;
std::vector<int>* get_plane_tri_indices(int idx);
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id);
std::vector<SurfaceFeature>* get_plane_features_pointer(unsigned int plane_id);
const indexed_triangle_set& get_its() const;
private:
void update_planes();
void extract_features(int plane_idx);
std::vector<PlaneData> m_planes;
std::vector<size_t> m_face_to_plane;
indexed_triangle_set m_its;
@ -158,7 +180,7 @@ void MeasuringImpl::update_planes()
m_planes.back().normal = normal_ptr->cast<double>();
std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end());
}
// Check that each facet is part of one of the planes.
assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); }));
@ -175,7 +197,7 @@ void MeasuringImpl::update_planes()
const auto& facets = planes[plane_id].facets;
planes[plane_id].borders.clear();
std::vector<std::array<bool, 3>> visited(facets.size(), {false, false, false});
for (int face_id=0; face_id<int(facets.size()); ++face_id) {
assert(face_to_plane[facets[face_id]] == plane_id);
@ -193,7 +215,7 @@ void MeasuringImpl::update_planes()
Halfedge_index he = sm.halfedge(Face_index(facets[face_id]));
while (he.side() != edge_id)
he = sm.next(he);
// he is the first halfedge on the border. Now walk around and append the points.
//const Halfedge_index he_orig = he;
planes[plane_id].borders.emplace_back();
@ -202,7 +224,7 @@ void MeasuringImpl::update_planes()
last_border.emplace_back(sm.point(sm.source(he)).cast<double>());
//Vertex_index target = sm.target(he);
const Halfedge_index he_start = he;
Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
assert(face_it != facets.end());
@ -228,7 +250,7 @@ void MeasuringImpl::update_planes()
he = sm.opposite(he);
if (he.is_invalid())
goto PLANE_FAILURE;
Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
if (face_it == facets.end() || *face_it != int(fi)) // This indicates a broken mesh.
@ -265,11 +287,6 @@ void MeasuringImpl::update_planes()
m_planes.shrink_to_fit();
}
void MeasuringImpl::extract_features(int plane_idx)
{
assert(! m_planes[plane_idx].features_extracted);
@ -490,7 +507,7 @@ void MeasuringImpl::extract_features(int plane_idx)
Vec3d cog = Vec3d::Zero();
size_t counter = 0;
for (const std::vector<Vec3d>& b : plane.borders) {
for (size_t i = 1; i < b.size(); ++i) {
for (size_t i = 0; i < b.size(); ++i) {
cog += b[i];
++counter;
}
@ -505,14 +522,7 @@ void MeasuringImpl::extract_features(int plane_idx)
plane.features_extracted = true;
}
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point)
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane)
{
if (face_idx >= m_face_to_plane.size())
return std::optional<SurfaceFeature>();
@ -521,7 +531,7 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
if (! plane.features_extracted)
extract_features(m_face_to_plane[face_idx]);
size_t closest_feature_idx = size_t(-1);
double min_dist = std::numeric_limits<double>::max();
@ -530,40 +540,57 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
assert(plane.surface_features.empty() || plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
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->dist;
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature_idx = i;
if (!only_select_plane) {
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->dist;
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature_idx = i;
}
}
}
}
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. Close = 10% of the lenghth of
// the edge, clamped between 0.025 and 0.5 mm.
const auto& [sp, ep] = f.get_edge();
double len_sq = (ep-sp).squaredNorm();
double limit_sq = std::max(0.025*0.025, std::min(0.5*0.5, 0.1 * 0.1 * len_sq));
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. Close = 10% of the lenghth of
// the edge, clamped between 0.025 and 0.5 mm.
const auto &[sp, ep] = f.get_edge();
double len_sq = (ep - sp).squaredNorm();
double limit_sq = std::max(0.025 * 0.025, std::min(0.5 * 0.5, 0.1 * 0.1 * len_sq));
if ((point - sp).squaredNorm() < limit_sq) {
SurfaceFeature local_f(sp);
local_f.origin_surface_feature = std::make_shared<SurfaceFeature>(local_f);
local_f.translate(world_tran);
return std::make_optional(local_f);
}
if ((point-sp).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(sp));
if ((point-ep).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(ep));
if ((point - ep).squaredNorm() < limit_sq) {
SurfaceFeature local_f(ep);
local_f.origin_surface_feature = std::make_shared<SurfaceFeature>(local_f);
local_f.translate(world_tran);
return std::make_optional(local_f);
}
}
SurfaceFeature f_tran(f);
f_tran.origin_surface_feature = std::make_shared<SurfaceFeature>(f);
f_tran.translate(world_tran);
return std::make_optional(f_tran);
}
return std::make_optional(f);
}
// Nothing detected, return the plane as a whole.
assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
return std::make_optional(plane.surface_features.back());
auto cur_plane = const_cast<PlaneData*>(&plane);
SurfaceFeature f_tran(cur_plane->surface_features.back());
f_tran.origin_surface_feature = std::make_shared<SurfaceFeature>(cur_plane->surface_features.back());
f_tran.translate(world_tran);
return std::make_optional(f_tran);
}
@ -583,6 +610,12 @@ const std::vector<int>& MeasuringImpl::get_plane_triangle_indices(int idx) const
return m_planes[idx].facets;
}
std::vector<int>* MeasuringImpl::get_plane_tri_indices(int idx)
{
assert(idx >= 0 && idx < int(m_planes.size()));
return &m_planes[idx].facets;
}
const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned int plane_id)
{
assert(plane_id < m_planes.size());
@ -591,21 +624,18 @@ const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned in
return m_planes[plane_id].surface_features;
}
std::vector<SurfaceFeature>* MeasuringImpl::get_plane_features_pointer(unsigned int plane_id) {
assert(plane_id < m_planes.size());
if (!m_planes[plane_id].features_extracted)
extract_features(plane_id);
return &m_planes[plane_id].surface_features;
}
const indexed_triangle_set& MeasuringImpl::get_its() const
{
return this->m_its;
}
Measuring::Measuring(const indexed_triangle_set& its)
: priv{std::make_unique<MeasuringImpl>(its)}
{}
@ -614,9 +644,12 @@ Measuring::~Measuring() {}
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d& point) const
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran, bool only_select_plane) const
{
return priv->get_feature(face_idx, point);
if (face_idx == 7516 || face_idx == 7517) {
std::cout << "";
}
return priv->get_feature(face_idx, point, world_tran, only_select_plane);
}
@ -796,13 +829,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple<int, Vec3d, Vec3d>& p1,
return ret;
}
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring)
MeasurementResult get_measurement(const SurfaceFeature &a, const SurfaceFeature &b, bool deal_circle_result)
{
assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef);
@ -819,7 +846,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (f2.get_type() == SurfaceFeatureType::Point) {
Vec3d diff = (f2.get_point() - f1.get_point());
result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()});
result.distance_xyz = diff.cwiseAbs();
result.distance_xyz = diff;
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Edge) {
@ -849,13 +876,18 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.distance_strict = std::make_optional(DistAndPoints{ radius, c, p_on_circle });
}
else {
const Eigen::Hyperplane<double, 3> circle_plane(n, c);
const Vec3d proj = circle_plane.projection(f1.get_point());
const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) +
(f1.get_point() - proj).squaredNorm());
if (deal_circle_result == false) {
const Eigen::Hyperplane<double, 3> circle_plane(n, c);
const Vec3d proj = circle_plane.projection(f1.get_point());
const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm());
const Vec3d p_on_circle = c + radius * (proj - c).normalized();
result.distance_strict = std::make_optional(DistAndPoints{ dist, f1.get_point(), p_on_circle }); // TODO
const Vec3d p_on_circle = c + radius * (proj - c).normalized();
result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle});
}
else {
const double dist = (f1.get_point() - c).norm();
result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), c});
}
}
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
@ -903,31 +935,31 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
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 = e1e2.normalized();
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 = e1e2.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);
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});
auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints &item1, const DistAndPoints &item2) { return item1.dist < item2.dist; });
if (deal_circle_result == false) {
result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to});
}
else{
const double dist = (it->from - center).norm();
result.distance_infinite = std::make_optional(DistAndPoints{dist, it->from, center});
}
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
assert(measuring != nullptr);
const auto [from, to] = f1.get_edge();
const auto [idx, normal, origin] = f2.get_plane();
@ -944,9 +976,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to });
}
else {
const std::vector<SurfaceFeature>& plane_features = measuring->get_plane_features(idx);
auto plane_features = f2.world_plane_features;
std::vector<DistAndPoints> distances;
for (const SurfaceFeature& sf : plane_features) {
for (const SurfaceFeature& sf : *plane_features) {
if (sf.get_type() == SurfaceFeatureType::Edge) {
const auto m = get_measurement(sf, f1);
if (!m.distance_infinite.has_value()) {
@ -975,7 +1007,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
const auto [c0, r0, n0] = f1.get_circle();
const auto [c1, r1, n1] = f2.get_circle();
// The following code is an adaptation of the algorithm found in:
// The following code is an adaptation of the algorithm found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h
// and described in:
// https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf
@ -1120,7 +1152,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
}
else {
ClosestInfo& info = candidates[0];
const double N0dD = n0.dot(D);
const Vec3d normProj = N0dD * n0;
const Vec3d compProj = D - normProj;
@ -1185,22 +1217,26 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
}
}
info.sqrDistance = distance * distance + N0dD * N0dD;
info.sqrDistance = distance * distance;
}
if (deal_circle_result == false) {
result.distance_infinite = std::make_optional(
DistAndPoints{std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest}); // TODO
} else {
const double dist = (c0 - c1).norm();
result.distance_strict = std::make_optional(DistAndPoints{dist, c0, c1});
}
result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
assert(measuring != nullptr);
const auto [center, radius, normal1] = f1.get_circle();
const auto [idx2, normal2, origin2] = f2.get_plane();
const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane<double, 3>(normal1, center).absDistance(origin2) < EPSILON;
if (!coplanar) {
const std::vector<SurfaceFeature>& plane_features = measuring->get_plane_features(idx2);
auto plane_features = f2.world_plane_features;
std::vector<DistAndPoints> distances;
for (const SurfaceFeature& sf : plane_features) {
for (const SurfaceFeature& sf : *plane_features) {
if (sf.get_type() == SurfaceFeatureType::Edge) {
const auto m = get_measurement(sf, f1);
if (!m.distance_infinite.has_value()) {
@ -1218,6 +1254,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
});
result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to });
}
else {
const Eigen::Hyperplane<double, 3> plane(normal2, origin2);
result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(center), center, plane.projection(center)});
}
}
else {
result.distance_strict = std::make_optional(DistAndPoints{0, center, origin2});
}
}
///////////////////////////////////////////////////////////////////////////
@ -1229,23 +1272,159 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (are_parallel(normal1, normal2)) {
// The planes are parallel, calculate distance.
const Eigen::Hyperplane<double, 3> plane(normal1, pt1);
result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO
const Eigen::Hyperplane<double, 3> plane(normal2, pt2);
result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt1), pt1, plane.projection(pt1) });
}
else
result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane());
}
if (swap) {
auto swap_dist_and_points = [](DistAndPoints& dp) {
auto back = dp.to;
dp.to = dp.from;
dp.from = back;
};
if (result.distance_infinite.has_value()) {
swap_dist_and_points(*result.distance_infinite);
}
if (result.distance_strict.has_value()) {
swap_dist_and_points(*result.distance_strict);
}
}
return result;
}
bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b) {
const bool swap = int(a.get_type()) > int(b.get_type());
const SurfaceFeature &f1 = swap ? b : a;
const SurfaceFeature &f2 = swap ? a : b;
if (f1.get_type() == SurfaceFeatureType::Point){
if (f2.get_type() == SurfaceFeatureType::Point) {
return true;
}
}
else if (f1.get_type() == SurfaceFeatureType::Circle) {
if (f2.get_type() == SurfaceFeatureType::Circle) {
return true;
}
}
return false;
}
AssemblyAction get_assembly_action(const SurfaceFeature& a, const SurfaceFeature& b)
{
AssemblyAction action;
const SurfaceFeature &f1 = a;
const SurfaceFeature &f2 = b;
if (f1.get_type() == SurfaceFeatureType::Plane) {
action.can_set_feature_1_reverse_rotation = true;
if (f2.get_type() == SurfaceFeatureType::Plane) {
const auto [idx1, normal1, pt1] = f1.get_plane();
const auto [idx2, normal2, pt2] = f2.get_plane();
action.can_set_to_center_coincidence = true;
action.can_set_feature_2_reverse_rotation = true;
if (are_parallel(normal1, normal2)) {
action.can_set_to_parallel = false;
action.has_parallel_distance = true;
action.can_around_center_of_faces = true;
Vec3d proj_pt2;
Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2);
action.parallel_distance = (pt2 - proj_pt2).norm();
if ((pt2 - proj_pt2).dot(normal1) < 0) {
action.parallel_distance = -action.parallel_distance;
}
action.angle_radian = 0;
} else {
action.can_set_to_parallel = true;
action.has_parallel_distance = false;
action.can_around_center_of_faces = false;
action.parallel_distance = 0;
action.angle_radian = std::acos(std::clamp(normal2.dot(-normal1), -1.0, 1.0));
}
}
}
return action;
}
void SurfaceFeature::translate(const Vec3d& displacement) {
switch (get_type()) {
case Measure::SurfaceFeatureType::Point: {
m_pt1 = m_pt1 + displacement;
break;
}
case Measure::SurfaceFeatureType::Edge: {
m_pt1 = m_pt1 + displacement;
m_pt2 = m_pt2 + displacement;
if (m_pt3.has_value()) { //extra_point()
m_pt3 = *m_pt3 + displacement;
}
break;
}
case Measure::SurfaceFeatureType::Plane: {
//m_pt1 is normal;
m_pt2 = m_pt2 + displacement;
break;
}
case Measure::SurfaceFeatureType::Circle: {
m_pt1 = m_pt1 + displacement;
// m_pt2 is normal;
break;
}
default: break;
}
}
void SurfaceFeature::translate(const Transform3d &tran)
{
switch (get_type()) {
case Measure::SurfaceFeatureType::Point: {
m_pt1 = tran * m_pt1;
break;
}
case Measure::SurfaceFeatureType::Edge: {
m_pt1 = tran * m_pt1;
m_pt2 = tran * m_pt2;
if (m_pt3.has_value()) { // extra_point()
m_pt3 = tran * *m_pt3;
}
break;
}
case Measure::SurfaceFeatureType::Plane: {
// m_pt1 is normal;
Vec3d temp_pt1 = m_pt2 + m_pt1;
temp_pt1 = tran * temp_pt1;
m_pt2 = tran * m_pt2;
m_pt1 = (temp_pt1 - m_pt2).normalized();
break;
}
case Measure::SurfaceFeatureType::Circle: {
// m_pt1 is center;
// m_pt2 is normal;
auto local_normal = m_pt2;
auto local_center = m_pt1;
Vec3d temp_pt2 = local_normal + local_center;
temp_pt2 = tran * temp_pt2;
m_pt1 = tran * m_pt1;
auto world_center = m_pt1;
m_pt2 = (temp_pt2 - m_pt1).normalized();
} // namespace Measure
auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center](const Vec3d &pt, double &value) {
Vec3d intersection_pt;
get_point_projection_to_plane(pt, local_center, local_normal, intersection_pt);
auto local_radius_pt = (intersection_pt - local_center).normalized() * value + local_center;
auto radius_pt = tran * local_radius_pt;
value = (radius_pt - world_center).norm();
};
//m_value is radius
auto new_pt = get_one_point_in_plane(local_center, local_normal);
calc_world_radius(new_pt, m_value);
break;
}
default: break;
}
}
}//namespace Measure
} // namespace Slic3r

View file

@ -6,18 +6,13 @@
#include "Point.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class TriangleMesh;
namespace Measure {
enum class SurfaceFeatureType : int {
Undef = 0,
Point = 1 << 0,
@ -26,22 +21,44 @@ enum class SurfaceFeatureType : int {
Plane = 1 << 3
};
class SurfaceFeature {
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt);
Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal);
class SurfaceFeature
{
public:
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
explicit SurfaceFeature(const Vec3d& pt)
SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
SurfaceFeature(const SurfaceFeature& sf){
this->clone(sf);
volume = sf.volume;
plane_indices = sf.plane_indices;
world_tran = sf.world_tran;
world_plane_features = sf.world_plane_features;
origin_surface_feature = sf.origin_surface_feature;
}
void clone(const SurfaceFeature &sf)
{
m_type = sf.get_type();
m_pt1 = sf.get_pt1();
m_pt2 = sf.get_pt2();
m_pt3 = sf.get_pt3();
m_value = sf.get_value();
}
void translate(const Vec3d& displacement);
void translate(const Transform3d& tran);
// 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<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
std::pair<Vec3d, Vec3d> 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<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
@ -75,6 +92,17 @@ public:
return !operator == (other);
}
void* volume{nullptr};
std::vector<int>* plane_indices{nullptr};
Transform3d world_tran;
std::shared_ptr<std::vector<SurfaceFeature>> world_plane_features{nullptr};
std::shared_ptr<SurfaceFeature> origin_surface_feature{nullptr};
Vec3d get_pt1() const{ return m_pt1; }
Vec3d get_pt2() const { return m_pt2; }
const std::optional<Vec3d>& get_pt3() const { return m_pt3; }
double get_value() const { return m_value; }
private:
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
Vec3d m_pt1{ Vec3d::Zero() };
@ -97,7 +125,7 @@ public:
// Given a face_idx where the mouse cursor points, return a feature that
// should be highlighted (if any).
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point, const Transform3d & world_tran,bool only_select_plane) const;
// Return total number of planes.
int get_num_of_planes() const;
@ -111,7 +139,7 @@ public:
// Returns the mesh used for measuring
const indexed_triangle_set& get_its() const;
private:
private:
std::unique_ptr<MeasuringImpl> priv;
};
@ -152,7 +180,24 @@ struct MeasurementResult {
};
// Returns distance/angle between two SurfaceFeatures.
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b,bool deal_circle_result =false);
bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b);
struct AssemblyAction
{
bool can_set_to_parallel{false};
bool can_set_to_center_coincidence{false};
bool can_set_feature_1_reverse_rotation{false};
bool can_set_feature_2_reverse_rotation{false};
bool can_around_center_of_faces{false};
bool has_parallel_distance{false};
float parallel_distance;
float angle_radian{0};
Transform3d tran_for_parallel;
Transform3d tran_for_center_coincidence;
Transform3d tran_for_reverse_rotation;
};
AssemblyAction get_assembly_action(const SurfaceFeature &a, const SurfaceFeature &b);
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }

View file

@ -7,7 +7,7 @@ namespace Slic3r {
namespace Measure {
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
class Polynomial1
@ -174,7 +174,7 @@ inline Polynomial1 operator * (double scalar, const Polynomial1& p)
}
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
class RootsPolynomial
@ -242,7 +242,7 @@ public:
return false;
if (tmin >= tmax)
// Invalid ordering of interval endpoitns.
// Invalid ordering of interval endpoitns.
return false;
for (uint32_t i = 1; i <= maxIterations; ++i) {
@ -345,7 +345,7 @@ public:
}
};
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
// Construct a single vector orthogonal to the nonzero input vector. If

View file

@ -145,6 +145,8 @@ set(SLIC3R_GUI_SOURCES
#GUI/Gizmos/GLGizmoFaceDetector.hpp
GUI/Gizmos/GLGizmoMeasure.cpp
GUI/Gizmos/GLGizmoMeasure.hpp
GUI/Gizmos/GLGizmoAssembly.cpp
GUI/Gizmos/GLGizmoAssembly.hpp
GUI/Gizmos/GLGizmoSeam.cpp
GUI/Gizmos/GLGizmoSeam.hpp
#GUI/Gizmos/GLGizmoText.cpp

View file

@ -4445,7 +4445,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
#endif // ENABLE_RETINA_GL
if (!m_mouse.ignore_right_up) {
if (!m_mouse.ignore_right_up && m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined) {
//BBS post right click event
if (!m_hover_plate_idxs.empty()) {
post_event(RBtnPlateEvent(EVT_GLCANVAS_PLATE_RIGHT_CLICK, { logical_pos, m_hover_plate_idxs.front() }));

View file

@ -743,6 +743,7 @@ public:
m_scene_raycaster.set_gizmos_on_top(value);
}
float get_explosion_ratio() { return m_explosion_ratio; }
void reset_explosion_ratio() { m_explosion_ratio = 1.0; }
void on_change_color_mode(bool is_dark, bool reinit = true);
const bool get_dark_mode_status() { return m_is_dark; }

View file

@ -1412,5 +1412,84 @@ GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int sec
return data;
}
GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector<int>& triangle_indices, float normal_offset)
{
GLModel::Geometry init_data;
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
init_data.reserve_indices(3 * triangle_indices.size());
init_data.reserve_vertices(3 * triangle_indices.size());
unsigned int i = 0;
for (int idx : triangle_indices) {
Vec3f v0 = its.vertices[its.indices[idx][0]];
Vec3f v1 = its.vertices[its.indices[idx][1]];
Vec3f v2 = its.vertices[its.indices[idx][2]];
const Vec3f n = (v1 - v0).cross(v2 - v0).normalized();
if (std::abs(normal_offset) > 0.0) {
v0 = v0 + n * normal_offset;
v1 = v1 + n * normal_offset;
v2 = v2 + n * normal_offset;
}
init_data.add_vertex(v0, n);
init_data.add_vertex(v1, n);
init_data.add_vertex(v2, n);
init_data.add_triangle(i, i + 1, i + 2);
i += 3;
}
return init_data;
}
GLModel::Geometry init_torus_data(unsigned int primary_resolution,
unsigned int secondary_resolution,
const Vec3f & center,
float radius,
float thickness,
const Vec3f & model_axis,
const Transform3f &world_trafo)
{
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
GLModel::Geometry data;
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
data.reserve_vertices(torus_sector_count * section_sector_count);
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
// vertices
const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast<double>()).cast<float>() *
Eigen::Quaternion<float>::FromTwoVectors(Vec3f::UnitZ(), model_axis);
for (unsigned int i = 0; i < torus_sector_count; ++i) {
const float section_angle = torus_sector_step * i;
const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f);
const Vec3f local_section_center = radius * radius_dir;
const Vec3f world_section_center = local_to_world_matrix * local_section_center;
const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized();
const Vec3f world_section_normal = (Vec3f) (local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized();
const Vec3f base_v = thickness * radius_dir;
for (unsigned int j = 0; j < section_sector_count; ++j) {
const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v;
data.add_vertex(world_section_center + v, (Vec3f) v.normalized());
}
}
// triangles
for (unsigned int i = 0; i < torus_sector_count; ++i) {
const unsigned int ii = i * section_sector_count;
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
for (unsigned int j = 0; j < section_sector_count; ++j) {
const unsigned int j_next = (j + 1) % section_sector_count;
const unsigned int i0 = ii + j;
const unsigned int i1 = ii_next + j;
const unsigned int i2 = ii_next + j_next;
const unsigned int i3 = ii + j_next;
data.add_triangle(i0, i1, i2);
data.add_triangle(i0, i2, i3);
}
}
return data;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -247,7 +247,15 @@ namespace GUI {
// the origin of the torus is in its center
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness);
} // namespace GUI
GLModel::Geometry init_plane_data(const indexed_triangle_set &its, const std::vector<int> &triangle_indices,float normal_offset = 0.0f);
GLModel::Geometry init_torus_data(unsigned int primary_resolution,
unsigned int secondary_resolution,
const Vec3f & center,
float radius,
float thickness,
const Vec3f & model_axis,
const Transform3f &world_trafo);
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLModel_hpp_

View file

@ -0,0 +1,167 @@
#include "GLGizmoAssembly.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/MeasureUtils.hpp"
#include <imgui/imgui_internal.h>
#include <numeric>
#include <GL/glew.h>
#include <tbb/parallel_for.h>
#include <future>
#include <wx/clipbrd.h>
namespace Slic3r {
namespace GUI {
GLGizmoAssembly::GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) :
GLGizmoMeasure(parent, icon_filename, sprite_id)
{
m_measure_mode = EMeasureMode::ONLY_ASSEMBLY;
}
std::string GLGizmoAssembly::on_get_name() const
{
if (!on_is_activable() && m_state == EState::Off) {
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
return _u8L("Assemble") + ":\n" + _u8L("Please confirm explosion ratio = 1 and select at least two volumes.");
}
else {
return _u8L("Assemble") + ":\n" + _u8L("Please select at least two volumes.");
}
} else {
return _u8L("Assemble");
}
}
bool GLGizmoAssembly::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
if (selection.is_wipe_tower()) {
return false;
}
const int selection_volumes_count = 2;
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
if (abs(m_parent.get_explosion_ratio() - 1.0f) < 1e-2 && selection.volumes_count() >= selection_volumes_count) {
return true;
}
return false;
} else {
return selection.volumes_count() >= selection_volumes_count;
}
}
void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limit)
{
static std::optional<Measure::SurfaceFeature> last_feature;
static EMode last_mode = EMode::FeatureSelection;
static SelectedFeatures last_selected_features;
static float last_y = 0.0f;
static float last_h = 0.0f;
if (m_editing_distance)
return;
m_current_active_imgui_id = ImGui::GetActiveID();
// adjust window position to avoid overlap the view toolbar
const float win_h = ImGui::GetWindowHeight();
y = std::min(y, bottom_limit - win_h);
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
if (last_h != win_h || last_y != y) {
// ask canvas for another frame to render the window in the correct position
m_imgui->set_requires_extra_frame();
if (last_h != win_h)
last_h = win_h;
if (last_y != y)
last_y = y;
}
// Orca
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
init_render_input_window();
float moving_size = m_imgui->calc_text_size(_L("(Moving)")).x;
float combox_content_size = m_imgui->calc_text_size(_L("Point and point assembly")).x*1.1 + ImGui::GetStyle().FramePadding.x * 18.0f;
float caption_size = moving_size + 2 * m_space_size;
if (render_assembly_mode_combo(caption_size + 0.5 * m_space_size, combox_content_size)) {
;
}
show_selection_ui();
show_face_face_assembly_common();
ImGui::Separator();
show_face_face_assembly_senior();
show_distance_xyz_ui();
render_input_window_warning(m_same_model_object);
ImGui::Separator();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
float caption_max = 0.f;
float total_text_max = 0.f;
for (const auto &t : std::array<std::string, 3>{"point_selection", "reset", "unselect"}) {
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
}
show_tooltip_information(caption_max, x, get_cur_y);
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::PopStyleVar(2);
if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) {
// the dialog may have changed its size, ask for an extra frame to render it properly
last_feature = m_curr_feature;
last_mode = m_mode;
last_selected_features = m_selected_features;
m_imgui->set_requires_extra_frame();
}
m_last_active_item_imgui = m_current_active_imgui_id;
GizmoImguiEnd();
// Orca
ImGuiWrapper::pop_toolbar_style();
}
void GLGizmoAssembly::render_input_window_warning(bool same_model_object)
{
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) {
if (m_hit_different_volumes.size() == 2) {
if (same_model_object == false) {
m_imgui->warning_text(_L("Warning") + ": " +
_L("It is recommended to assemble the objects first,\nbecause the objects is restriced to bed \nand only parts can be lifted."));
}
}
}
}
bool GLGizmoAssembly::render_assembly_mode_combo(double label_width, float item_width)
{
ImGui::AlignTextToFramePadding();
int selection_idx = int(m_assembly_mode);
std::vector<std::string> modes = {_u8L("Face and face assembly"), _u8L("Point and point assembly")};
bool is_changed = false;
ImGuiWrapper::push_combo_style(m_parent.get_scale());
if (render_combo(_u8L("Mode"), modes, selection_idx, label_width, item_width)) {
is_changed = true;
switch_to_mode((AssemblyMode) selection_idx);
}
ImGuiWrapper::pop_combo_style();
return is_changed;
}
void GLGizmoAssembly::switch_to_mode(AssemblyMode new_mode)
{
m_assembly_mode = new_mode;
reset_all_feature();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -0,0 +1,43 @@
#ifndef slic3r_GLGizmoAssembly_hpp_
#define slic3r_GLGizmoAssembly_hpp_
#include "GLGizmoMeasure.hpp"
namespace Slic3r {
namespace GUI {
class GLGizmoAssembly : public GLGizmoMeasure
{
public:
GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
/// <summary>
/// Apply rotation on select plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
//bool on_mouse(const wxMouseEvent &mouse_event) override;
//void data_changed(bool is_serializing) override;
//bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override;
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Assembly gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Assembly gizmo"); }
protected:
//bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
//void on_render() override;
//void on_set_state() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
void render_input_window_warning(bool same_model_object) override;
bool render_assembly_mode_combo(double label_width, float item_width);
void switch_to_mode(AssemblyMode new_mode);
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoAssembly_hpp_

View file

@ -194,6 +194,39 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color)
}
}
bool GLGizmoBase::render_combo(const std::string &label, const std::vector<std::string> &lines, int &selection_idx, float label_width, float item_width)
{
ImGuiWrapper::push_combo_style(m_parent.get_scale());
ImGui::AlignTextToFramePadding();
m_imgui->text(label);
ImGui::SameLine(label_width);
ImGui::PushItemWidth(item_width);
size_t selection_out = selection_idx;
const char *selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
ImGui::PushID(int(line_idx));
if (ImGui::Selectable("", line_idx == selection_idx)) selection_out = line_idx;
ImGui::SameLine();
ImGui::Text("%s", lines[line_idx].c_str());
ImGui::PopID();
}
ImGui::EndCombo();
}
bool is_changed = selection_idx != selection_out;
selection_idx = selection_out;
//if (is_changed) update_connector_shape();
ImGuiWrapper::pop_combo_style();
return is_changed;
}
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: m_parent(parent)
, m_group_id(-1)

View file

@ -8,6 +8,7 @@
#include "slic3r/GUI/GLModel.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
#include "slic3r/GUI/SceneRaycaster.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include <cereal/archives/binary.hpp>
@ -148,6 +149,9 @@ protected:
bool m_is_dark_mode = false;
bool render_combo(const std::string &label, const std::vector<std::string> &lines,
int &selection_idx, float label_width, float item_width);
public:
GLGizmoBase(GLCanvas3D& parent,
const std::string& icon_filename,
@ -181,6 +185,13 @@ public:
virtual bool apply_clipping_plane() { return true; }
/// <summary>
/// Implement when want to process mouse events in gizmo
/// Click, Right click, move, drag, ...
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
unsigned int get_sprite_id() const { return m_sprite_id; }
int get_hover_id() const { return m_hover_id; }
@ -209,14 +220,6 @@ public:
/// </summary>
virtual void data_changed(bool is_serializing){};
/// <summary>
/// Implement when want to process mouse events in gizmo
/// Click, Right click, move, drag, ...
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); }
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }

View file

@ -520,40 +520,6 @@ bool GLGizmoCut3D::render_cut_mode_combo()
return is_changed;
}
bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, int& selection_idx)
{
ImGuiWrapper::push_combo_style(m_parent.get_scale());
ImGui::AlignTextToFramePadding();
m_imgui->text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_editing_window_width);
size_t selection_out = selection_idx;
const char* selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
ImGui::PushID(int(line_idx));
if (ImGui::Selectable("", line_idx == selection_idx))
selection_out = line_idx;
ImGui::SameLine();
ImGui::Text("%s", lines[line_idx].c_str());
ImGui::PopID();
}
ImGui::EndCombo();
}
bool is_changed = selection_idx != selection_out;
selection_idx = selection_out;
//if (is_changed) update_connector_shape();
ImGuiWrapper::pop_combo_style();
return is_changed;
}
bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_in)
{
ImGui::AlignTextToFramePadding();
@ -2299,7 +2265,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
m_connector_style = int(CutConnectorStyle::Prism);
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
}
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style))
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style, m_label_width, m_editing_window_width))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
m_imgui->disabled_end();
@ -2308,7 +2274,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
m_connector_shape_id = int(CutConnectorShape::Circle);
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
}
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id))
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id, m_label_width, m_editing_window_width))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
m_imgui->disabled_end();

View file

@ -335,7 +335,6 @@ private:
void set_center(const Vec3d&center, bool update_tbb = false);
void switch_to_mode(size_t new_mode);
bool render_cut_mode_combo();
bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
void render_move_center_input(int axis);

File diff suppressed because it is too large Load diff

View file

@ -12,16 +12,85 @@
namespace Slic3r {
enum class ModelVolumeType : int;
namespace Measure { class Measuring; }
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
enum class EMeasureMode : unsigned char {
ONLY_MEASURE,
ONLY_ASSEMBLY
};
enum class AssemblyMode : unsigned char {
FACE_FACE,
POINT_POINT,
};
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};
static const Slic3r::ColorRGBA NEUTRAL_COLOR = {0.5f, 0.5f, 0.5f, 1.0f};
static const Slic3r::ColorRGBA HOVER_COLOR = ColorRGBA::GREEN();
static const int POINT_ID = 100;
static const int EDGE_ID = 200;
static const int CIRCLE_ID = 300;
static const int PLANE_ID = 400;
static const int SEL_SPHERE_1_ID = 501;
static const int SEL_SPHERE_2_ID = 502;
static const float TRIANGLE_BASE = 10.0f;
static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f;
static const std::string CTRL_STR =
#ifdef __APPLE__
""
#else
"Ctrl"
#endif //__APPLE__
;
class TransformHelper
{
struct Cache
{
std::array<int, 4> viewport;
Matrix4d ndc_to_ss_matrix;
Transform3d ndc_to_ss_matrix_inverse;
};
static Cache s_cache;
public:
static Vec3d model_to_world(const Vec3d &model, const Transform3d &world_matrix);
static Vec4d world_to_clip(const Vec3d &world, const Matrix4d &projection_view_matrix);
static Vec3d clip_to_ndc(const Vec4d &clip);
static Vec2d ndc_to_ss(const Vec3d &ndc, const std::array<int, 4> &viewport);
static Vec4d model_to_clip(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix);
static Vec3d model_to_ndc(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix);
static Vec2d model_to_ss(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix, const std::array<int, 4> &viewport);
static Vec2d world_to_ss(const Vec3d &world, const Matrix4d &projection_view_matrix, const std::array<int, 4> &viewport);
static const Matrix4d & ndc_to_ss_matrix(const std::array<int, 4> &viewport);
static const Transform3d ndc_to_ss_matrix_inverse(const std::array<int, 4> &viewport);
private:
static void update(const std::array<int, 4> &viewport);
};
class GLGizmoMeasure : public GLGizmoBase
{
protected:
using PickRaycaster = SceneRaycasterItem;
enum GripperType {
UNDEFINE,
POINT,
EDGE,
CIRCLE,
CIRCLE_1,
CIRCLE_2,
PLANE,
PLANE_1,
PLANE_2,
SPHERE_1,
SPHERE_2,
};
enum class EMode : unsigned char
{
FeatureSelection,
@ -69,30 +138,50 @@ class GLGizmoMeasure : public GLGizmoBase
}
};
struct VolumeCacheItem
{
const ModelObject* object{ nullptr };
const ModelInstance* instance{ nullptr };
const ModelVolume* volume{ nullptr };
Transform3d world_trafo;
//struct VolumeCacheItem
//{
// const ModelObject* object{ nullptr };
// const ModelInstance* instance{ nullptr };
// const ModelVolume* volume{ nullptr };
// Transform3d world_trafo;
bool operator == (const VolumeCacheItem& other) const {
return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
this->world_trafo.isApprox(other.world_trafo);
}
};
// bool operator == (const VolumeCacheItem& other) const {
// return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
// this->world_trafo.isApprox(other.world_trafo);
// }
//};
std::vector<VolumeCacheItem> m_volumes_cache;
//std::vector<VolumeCacheItem> m_volumes_cache;
EMode m_mode{ EMode::FeatureSelection };
Measure::MeasurementResult m_measurement_result;
Measure::AssemblyAction m_assembly_action;
std::map<GLVolume*, std::shared_ptr<Measure::Measuring>> m_mesh_measure_map;
std::shared_ptr<Measure::Measuring> m_curr_measuring{nullptr};
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
//first feature
PickingModel m_sphere;
PickingModel m_cylinder;
PickingModel m_circle;
PickingModel m_plane;
struct CircleGLModel
{
PickingModel circle;
Measure::SurfaceFeature *last_circle_feature{nullptr};
float inv_zoom{0};
};
CircleGLModel m_curr_circle;
CircleGLModel m_feature_circle_first;
CircleGLModel m_feature_circle_second;
void init_circle_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, CircleGLModel &circle_gl_model, float inv_zoom);
struct PlaneGLModel {
int plane_idx{0};
PickingModel plane;
};
PlaneGLModel m_curr_plane;
PlaneGLModel m_feature_plane_first;
PlaneGLModel m_feature_plane_second;
void init_plane_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, PlaneGLModel &plane_gl_model);
struct Dimensioning
{
GLModel line;
@ -101,40 +190,38 @@ class GLGizmoMeasure : public GLGizmoBase
};
Dimensioning m_dimensioning;
// Uses a standalone raycaster and not the shared one because of the
// difference in how the mesh is updated
std::unique_ptr<MeshRaycaster> m_raycaster;
std::vector<GLModel> m_plane_models_cache;
std::map<int, std::shared_ptr<SceneRaycasterItem>> m_raycasters;
std::map<GLVolume*, std::shared_ptr<PickRaycaster>> m_mesh_raycaster_map;
std::map<GripperType, std::shared_ptr<PickRaycaster>> m_gripper_id_raycast_map;
std::vector<GLVolume*> m_hit_different_volumes;
std::vector<GLVolume*> m_hit_order_volumes;
GLVolume* m_last_hit_volume;
//std::vector<std::shared_ptr<GLModel>> m_plane_models_cache;
unsigned int m_last_active_item_imgui{0};
Vec3d m_buffered_distance;
Vec3d m_distance;
double m_buffered_parallel_distance{0};
double m_buffered_around_center{0};
// used to keep the raycasters for point/center spheres
std::vector<std::shared_ptr<SceneRaycasterItem>> m_selected_sphere_raycasters;
//std::vector<std::shared_ptr<PickRaycaster>> m_selected_sphere_raycasters;
std::optional<Measure::SurfaceFeature> m_curr_feature;
std::optional<Vec3d> m_curr_point_on_feature_position;
struct SceneRaycasterState
{
std::shared_ptr<SceneRaycasterItem> raycaster{ nullptr };
bool state{true};
};
std::vector<SceneRaycasterState> m_scene_raycasters;
// These hold information to decide whether recalculation is necessary:
float m_last_inv_zoom{ 0.0f };
std::optional<Measure::SurfaceFeature> m_last_circle;
std::optional<Measure::SurfaceFeature> m_last_circle_feature;
int m_last_plane_idx{ -1 };
bool m_mouse_left_down{ false }; // for detection left_up of this gizmo
Vec2d m_mouse_pos{ Vec2d::Zero() };
bool m_mouse_left_down_mesh_deal{false};//for pick mesh
KeyAutoRepeatFilter m_shift_kar_filter;
SelectedFeatures m_selected_features;
bool m_pending_scale{ false };
int m_pending_scale{ 0 };
bool m_set_center_coincidence{false};
bool m_editing_distance{ false };
bool m_is_editing_distance_first_frame{ true };
bool m_can_set_xyz_distance{false};
void update_if_needed();
void disable_scene_raycasters();
@ -148,7 +235,6 @@ class GLGizmoMeasure : public GLGizmoBase
public:
GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
/// <summary>
/// Apply rotation on select plane
/// </summary>
@ -158,12 +244,12 @@ public:
void data_changed(bool is_serializing) override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); }
std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
//std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
protected:
bool on_init() override;
@ -172,20 +258,66 @@ protected:
void on_render() override;
void on_set_state() override;
//virtual void on_render_for_picking() override;
void show_selection_ui();
void show_distance_xyz_ui();
//void show_point_point_assembly();
void show_face_face_assembly_common();
void show_face_face_assembly_senior();
void init_render_input_window();
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual void on_register_raycasters_for_picking() override;
virtual void on_unregister_raycasters_for_picking() override;
virtual void render_input_window_warning(bool same_model_object);
void remove_selected_sphere_raycaster(int id);
void update_measurement_result();
// Orca
void show_tooltip_information(float caption_max, float x, float y);
void reset_all_pick();
void reset_gripper_pick(GripperType id,bool is_all = false);
void register_single_mesh_pick();
//void update_single_mesh_pick(GLVolume* v);
private:
std::string format_double(double value);
std::string format_vec3(const Vec3d &v);
std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type);
std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id);
std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type);
bool is_feature_with_center(const Measure::SurfaceFeature &feature);
Vec3d get_feature_offset(const Measure::SurfaceFeature &feature);
void reset_all_feature();
void reset_feature1_render();
void reset_feature2_render();
void reset_feature1();
void reset_feature2();
bool is_two_volume_in_same_model_object();
Measure::Measuring* get_measuring_of_mesh(GLVolume *v, Transform3d &tran);
void update_world_plane_features(Measure::Measuring *cur_measuring, Measure::SurfaceFeature &feautre);
void update_feature_by_tran(Measure::SurfaceFeature & feature);
void set_distance(bool same_model_object, const Vec3d &displacement, bool take_shot = true);
void set_to_parallel(bool same_model_object, bool take_shot = true, bool is_anti_parallel = false);
void set_to_reverse_rotation(bool same_model_object,int feature_index);
void set_to_around_center_of_faces(bool same_model_object,float rotate_degree);
void set_to_center_coincidence(bool same_model_object);
void set_parallel_distance(bool same_model_object,float dist);
bool is_pick_meet_assembly_mode(const SelectedFeatures::Item& item);
protected:
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
bool m_show_reset_first_tip{false};
bool m_selected_wrong_feature_waring_tip{false};
EMeasureMode m_measure_mode{EMeasureMode::ONLY_MEASURE};
AssemblyMode m_assembly_mode{AssemblyMode::FACE_FACE};
bool m_flip_volume_2{false};
float m_space_size;
float m_input_size_max;
bool m_use_inches;
bool m_only_select_plane{false};
std::string m_units;
mutable bool m_same_model_object;
mutable unsigned int m_current_active_imgui_id;
};
} // namespace GUI

View file

@ -23,8 +23,8 @@
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoAssembly.hpp"
#include "libslic3r/format.hpp"
#include "libslic3r/Model.hpp"
@ -61,7 +61,9 @@ std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
out.reserve(m_gizmos.size());
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
for (size_t i = 0; i < m_gizmos.size(); ++i)
if (m_gizmos[i]->get_sprite_id() == (unsigned int)MmuSegmentation)
if (m_gizmos[i]->get_sprite_id() == (unsigned int) Measure ||
m_gizmos[i]->get_sprite_id() == (unsigned int) Assembly ||
m_gizmos[i]->get_sprite_id() == (unsigned int) MmuSegmentation)
out.push_back(i);
}
else {
@ -158,9 +160,12 @@ void GLGizmosManager::switch_gizmos_icon_filename()
case(EType::MeshBoolean):
gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg");
break;
case(EType::Measure):
case (EType::Measure):
gizmo->set_icon_filename(m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg");
break;
case (EType::Assembly):
gizmo->set_icon_filename(m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg");
break;
}
}
@ -199,6 +204,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss));
m_gizmos.emplace_back(new GLGizmoSVG(m_parent));
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure));
m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
@ -342,6 +348,8 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
void GLGizmosManager::set_hover_id(int id)
{
// Measure and assembly handles hover by itself
if (m_current == EType::Measure || m_current == EType::Assembly) { return; }
if (!m_enabled || m_current == Undefined)
return;
@ -435,7 +443,9 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
else if (m_current == MmuSegmentation)
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Measure)
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
return dynamic_cast<GLGizmoMeasure *>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Assembly)
return dynamic_cast<GLGizmoAssembly *>(m_gizmos[Assembly].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == Cut)
return dynamic_cast<GLGizmoCut3D*>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MeshBoolean)
@ -656,6 +666,11 @@ bool GLGizmosManager::on_mouse(const wxMouseEvent &mouse_event)
// &m_gizmos[m_current]->on_mouse != &GLGizmoBase::on_mouse &&
m_gizmos[m_current]->on_mouse(mouse_event))
return true;
if (mouse_event.RightUp() && m_current != EType::Undefined && !m_parent.is_mouse_dragging()) {
// Prevent default right context menu in gizmos
return true;
}
return false;
}
@ -693,7 +708,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
case WXK_ESCAPE:
{
if (m_current != Undefined) {
if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) {
if ((m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Escape)) {
// do nothing
} else
//if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
@ -703,14 +718,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
}
break;
}
case WXK_BACK:
case WXK_DELETE:
{
if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete))
processed = true;
break;
}
//skip some keys when gizmo
case 'A':
case 'a':
{
@ -737,14 +745,12 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
//}
//case WXK_BACK:
//case WXK_DELETE:
//{
// if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete))
// processed = true;
// break;
//}
case WXK_BACK:
case WXK_DELETE: {
if ((m_current == Cut || m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Delete))
processed = true;
break;
}
//case 'A':
//case 'a':
//{
@ -839,13 +845,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
processed = true;
}
}*/
if (m_current == Measure) {
if (m_current == Measure || m_current == Assembly) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
}
// if (processed)
// m_parent.set_cursor(GLCanvas3D::Standard);
}
@ -927,8 +932,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
// force extra frame to automatically update window size
wxGetApp().imgui()->set_requires_extra_frame();
}
}
else if (m_current == Measure) {
} else if (m_current == Measure || m_current == Assembly) {
if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)

View file

@ -86,6 +86,7 @@ public:
Emboss,
Svg,
Measure,
Assembly,
Simplify,
//SlaSupports,
// BBS

View file

@ -161,7 +161,9 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT;
const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = COL_BUTTON_HOVERED;
//BBS
const ImVec4 ImGuiWrapper::COL_RED = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
const ImVec4 ImGuiWrapper::COL_GREEN = ImVec4(0.0f, 1.0f, 0.0f, 1.0f);
const ImVec4 ImGuiWrapper::COL_BLUE = ImVec4(0.0f, 0.0f, 1.0f, 1.0f);
const ImVec4 ImGuiWrapper::COL_BLUE_LIGHT = ImVec4(0.122f, 0.557f, 0.918f, 1.0f);
const ImVec4 ImGuiWrapper::COL_GREEN_LIGHT = { 0.f, 156 / 255.f, 136 / 255.f, 0.25f }; // ORCA used on various places like text selection bg. Replaced with orca color
const ImVec4 ImGuiWrapper::COL_HOVER = { 0.933f, 0.933f, 0.933f, 1.0f };
@ -856,6 +858,10 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active)
return ImGui::RadioButton(label_utf8.c_str(), active);
}
ImVec4 ImGuiWrapper::to_ImVec4(const ColorRGB &color) {
return {color.r(), color.g(), color.b(), 1.0};
}
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
{
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
@ -943,6 +949,19 @@ void ImGuiWrapper::text(const wxString &label)
ImGuiWrapper::text(label_utf8.c_str());
}
void ImGuiWrapper::warning_text(const char *label)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(ColorRGB::WARNING()));
this->text(label);
ImGui::PopStyleColor();
}
void ImGuiWrapper::warning_text(const wxString &all_text)
{
auto label_utf8 = into_u8(all_text);
warning_text(label_utf8.c_str());
}
void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
{
ImGui::TextColored(color, "%s", label);

View file

@ -134,6 +134,7 @@ public:
bool button(const wxString& label, float width, float height);
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
bool radio_button(const wxString &label, bool active);
static ImVec4 to_ImVec4(const ColorRGB &color);
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
@ -144,6 +145,8 @@ public:
static void text(const char *label);
static void text(const std::string &label);
static void text(const wxString &label);
void warning_text(const char *all_text);
void warning_text(const wxString &all_text);
static void text_colored(const ImVec4& color, const char* label);
static void text_colored(const ImVec4& color, const std::string& label);
static void text_colored(const ImVec4& color, const wxString& label);
@ -323,6 +326,9 @@ public:
static const ImVec4 COL_BUTTON_ACTIVE;
//BBS add more colors
static const ImVec4 COL_RED;
static const ImVec4 COL_GREEN;
static const ImVec4 COL_BLUE;
static const ImVec4 COL_BLUE_LIGHT;
static const ImVec4 COL_GREEN_LIGHT;
static const ImVec4 COL_HOVER;

View file

@ -21,6 +21,9 @@ class SceneRaycasterItem
Transform3d m_trafo;
public:
SceneRaycasterItem(int id, const MeshRaycaster& raycaster)
: m_id(id), m_raycaster(&raycaster), m_trafo(Transform3d::Identity()), m_use_back_faces(false)
{}
SceneRaycasterItem(int id, const MeshRaycaster& raycaster, const Transform3d& trafo, bool use_back_faces = false)
: m_id(id), m_raycaster(&raycaster), m_trafo(trafo), m_use_back_faces(use_back_faces)
{}

View file

@ -1720,6 +1720,66 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
this->set_bounding_boxes_dirty();
}
void Selection::translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement) {
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx)
v.set_volume_offset(v.get_volume_offset() + displacement);
}
this->set_bounding_boxes_dirty();
}
void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran)
{
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx) {
v.set_instance_transformation(overwrite_tran);
}
}
std::set<unsigned int> done; // prevent processing volumes twice
done.insert(m_list.begin(), m_list.end());
for (unsigned int i : m_list) {
if (done.size() == m_volumes->size()) break;
int object_idx = (*m_volumes)[i]->object_idx();
if (object_idx >= 1000) continue;
// Process unselected volumes of the object.
for (unsigned int j = 0; j < (unsigned int) m_volumes->size(); ++j) {
if (done.size() == m_volumes->size()) break;
if (done.find(j) != done.end()) continue;
GLVolume &v = *(*m_volumes)[j];
if (v.object_idx() != object_idx || v.instance_idx() != (int) instance_idx)
continue;
v.set_instance_transformation(overwrite_tran);
done.insert(j);
}
}
this->set_bounding_boxes_dirty();
}
void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran)
{
if (!m_valid) return;
for (unsigned int i : m_list) {
GLVolume &v = *(*m_volumes)[i];
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx) {
v.set_volume_transformation(overwrite_tran);
}
}
this->set_bounding_boxes_dirty();
}
//BBS: add partplate related logic
void Selection::notify_instance_update(int object_idx, int instance_idx)
{

View file

@ -344,6 +344,10 @@ public:
void translate(unsigned int object_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement);
void rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran);
void rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran);
//BBS: add partplate related logic
void notify_instance_update(int object_idx, int instance_idx);
// BBS