#ifndef slic3r_MeshUtils_hpp_ #define slic3r_MeshUtils_hpp_ #include "libslic3r/Point.hpp" #include "libslic3r/Color.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/SLA/IndexedMesh.hpp" #include "admesh/stl.h" #include "slic3r/GUI/3DScene.hpp" #include namespace Slic3r { namespace GUI { struct Camera; // lm_FIXME: Following class might possibly be replaced by Eigen::Hyperplane class ClippingPlane { double m_data[4]; public: ClippingPlane() { *this = ClipsNothing(); } ClippingPlane(const Vec3d& direction, double offset) { set_normal(direction); set_offset(offset); } bool operator==(const ClippingPlane& cp) const { return m_data[0]==cp.m_data[0] && m_data[1]==cp.m_data[1] && m_data[2]==cp.m_data[2] && m_data[3]==cp.m_data[3]; } bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); } double distance(const Vec3d& pt) const { // FIXME: this fails: assert(is_approx(get_normal().norm(), 1.)); return (-get_normal().dot(pt) + m_data[3]); } bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; } void set_normal(const Vec3d& normal) { const Vec3d norm_dir = normal.normalized(); m_data[0] = norm_dir.x(); m_data[1] = norm_dir.y(); m_data[2] = norm_dir.z(); } void set_offset(double offset) { m_data[3] = offset; } double get_offset() const { return m_data[3]; } Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); } void invert_normal(){ m_data[0] *= -1.0; m_data[1] *= -1.0; m_data[2] *= -1.0; } ClippingPlane inverted_normal() const { return ClippingPlane(-get_normal(), get_offset()); } bool is_active() const { return m_data[3] != DBL_MAX; } static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); } const double* get_data() const { return m_data; } // Serialization through cereal library template void serialize( Archive & ar ) { ar( m_data[0], m_data[1], m_data[2], m_data[3] ); } }; // MeshClipper class cuts a mesh and is able to return a triangulated cut. class MeshClipper { public: ~MeshClipper(); // Set whether the cut should be triangulated and whether a cut // contour should be calculated and shown. void set_behaviour(bool fill_cut, double contour_width); // Inform MeshClipper about which plane we want to use to cut the mesh // This is supposed to be in world coordinates. void set_plane(const ClippingPlane& plane); // In case the object is clipped by two planes (e.g. in case of sinking // objects), this will be used to clip the triagnulated cut. // Pass ClippingPlane::ClipsNothing to turn this off. void set_limiting_plane(const ClippingPlane& plane); // Which mesh to cut. MeshClipper remembers const * to it, caller // must make sure that it stays valid. void set_mesh(const TriangleMesh& mesh); void set_negative_mesh(const TriangleMesh &mesh); // Inform the MeshClipper about the transformation that transforms the mesh // into world coordinates. void set_transformation(const Geometry::Transformation& trafo); // Render the triangulated cut. Transformation matrices should // be set in world coords. void render_cut(const ColorRGBA &color, const std::vector *ignore_idxs = nullptr); void render_contour(const ColorRGBA &color, const std::vector *ignore_idxs = nullptr); int is_projection_inside_cut(const Vec3d &point) const; bool has_valid_contour() const; int get_number_of_contours() const { return m_result ? m_result->cut_islands.size() : 0; } std::vector point_per_contour() const; private: void recalculate_triangles(); void reset(); Geometry::Transformation m_trafo; const TriangleMesh * m_mesh = nullptr; const TriangleMesh * m_negative_mesh = nullptr; ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); /*std::vector m_triangles2d; GLIndexedVertexArray m_vertex_array; bool m_triangles_valid = false;*/ struct CutIsland { ExPolygon expoly; BoundingBox expoly_bb; GLModel model; GLModel model_expanded; bool disabled = false; size_t hash; }; struct ClipResult { std::vector cut_islands; Transform3d trafo; // this rotates the cut into world coords }; std::optional m_result; // the cut plane bool m_fill_cut = true; double m_contour_width = 0.; }; // MeshRaycaster class answers queries such as where on the mesh someone clicked, // whether certain points are visible or obscured by the mesh etc. class MeshRaycaster { public: // The class references extern TriangleMesh, which must stay alive // during MeshRaycaster existence. MeshRaycaster(const TriangleMesh &mesh); static void line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, const Camera &camera, Vec3d &point, Vec3d &direction); void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction) const; // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( const Vec2d& mouse_pos, const Transform3d& trafo, // how to get the mesh into world coords const Camera& camera, // current camera position Vec3f& position, // where to save the positibon of the hit (mesh coords) Vec3f& normal, // normal of the triangle that was hit const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) size_t* facet_idx = nullptr, // index of the facet hit bool sinking_limit = true ) const; const sla::IndexedMesh &get_aabb_mesh() const { return m_emesh; } // Given a point and direction in world coords, returns whether the respective line // intersects the mesh if it is transformed into world by trafo. bool intersects_line(Vec3d point, Vec3d direction, const Transform3d &trafo) const; // Given a vector of points in woorld coordinates, this returns vector // of indices of points that are visible (i.e. not cut by clipping plane // or obscured by part of the mesh. std::vector get_unobscured_idxs( const Geometry::Transformation& trafo, // how to get the mesh into world coords const Camera& camera, // current camera position const std::vector& points, // points in world coords const ClippingPlane* clipping_plane = nullptr // clipping plane (if active) ) const; // Returns true if the ray, built from mouse position and camera direction, intersects the mesh. // In this case, position and normal contain the position and normal, in model coordinates, of the intersection closest to the camera, // depending on the position/orientation of the clipping_plane, if specified bool closest_hit(const Vec2d & mouse_pos, const Transform3d & trafo, // how to get the mesh into world coords const Camera & camera, // current camera position Vec3f & position, // where to save the positibon of the hit (mesh coords) Vec3f & normal, // normal of the triangle that was hit const ClippingPlane *clipping_plane = nullptr, // clipping plane (if active) size_t * facet_idx = nullptr // index of the facet hit ) const; // Given a point in world coords, the method returns closest point on the mesh. // The output is in mesh coords. // normal* can be used to also get normal of the respective triangle. Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; // Given a point in mesh coords, the method returns the closest facet from mesh. int get_closest_facet(const Vec3f &point) const; Vec3f get_triangle_normal(size_t facet_idx) const; private: sla::IndexedMesh m_emesh; std::vector m_normals; }; class PickRaycaster { public: //PickRaycaster(TriangleMesh *mesh) { // mesh_raycaster = std::make_shared(*mesh); //} /*PickRaycaster(TriangleMesh *mesh, const Transform3d &tran) : PickRaycaster(mesh) { set_transform(tran); }*/ PickRaycaster(TriangleMesh *mesh, int _id) { mesh_raycaster = std::make_shared(*mesh); m_id = _id; } PickRaycaster(TriangleMesh *mesh, int _id, const Transform3d &tran) { mesh_raycaster = std::make_shared(*mesh); set_transform(tran); m_id = _id; } void set_transform(const Transform3d &tran) { world_tran.set_from_transform(tran); } std::shared_ptr mesh_raycaster{nullptr}; Geometry::Transformation world_tran; bool is_active() const { return m_active; } void set_active(bool active) { m_active = active; } int get_id() { return m_id; } private: bool m_active{true}; int m_id{-1}; }; } // namespace GUI } // namespace Slic3r #endif // slic3r_MeshUtils_hpp_