mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	SLA support points improvements
- semi-intelligent algorithm to place support points - enhanced ImGui dialog with editing/non-editing mode - support points can have different head diameter (only implemented in GUI so far) - autogenerated points supporting emerging islands are annotated and the info is kept
This commit is contained in:
		
							parent
							
								
									f4243c694f
								
							
						
					
					
						commit
						21026ec9a8
					
				
					 16 changed files with 433 additions and 495 deletions
				
			
		| 
						 | 
				
			
			@ -323,7 +323,7 @@ namespace Slic3r {
 | 
			
		|||
        typedef std::map<int, ObjectMetadata> IdToMetadataMap;
 | 
			
		||||
        typedef std::map<int, Geometry> IdToGeometryMap;
 | 
			
		||||
        typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
 | 
			
		||||
        typedef std::map<int, std::vector<Vec3f>> IdToSlaSupportPointsMap;
 | 
			
		||||
        typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
 | 
			
		||||
 | 
			
		||||
        // Version of the 3mf file
 | 
			
		||||
        unsigned int m_version;
 | 
			
		||||
| 
						 | 
				
			
			@ -811,10 +811,14 @@ namespace Slic3r {
 | 
			
		|||
                std::vector<std::string> object_data_points;
 | 
			
		||||
                boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
 | 
			
		||||
 | 
			
		||||
                std::vector<Vec3f> sla_support_points;
 | 
			
		||||
                std::vector<sla::SupportPoint> sla_support_points;
 | 
			
		||||
 | 
			
		||||
                for (unsigned int i=0; i<object_data_points.size(); i+=3)
 | 
			
		||||
                    sla_support_points.push_back(Vec3d(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())).cast<float>());
 | 
			
		||||
                for (unsigned int i=0; i<object_data_points.size(); i+=5)
 | 
			
		||||
                    sla_support_points.emplace_back(std::atof(object_data_points[i+0].c_str()),
 | 
			
		||||
                                                    std::atof(object_data_points[i+1].c_str()),
 | 
			
		||||
                                                    std::atof(object_data_points[i+2].c_str()),
 | 
			
		||||
                                                    std::atof(object_data_points[i+3].c_str()),
 | 
			
		||||
                                                    std::atof(object_data_points[i+4].c_str()));
 | 
			
		||||
 | 
			
		||||
                if (!sla_support_points.empty())
 | 
			
		||||
                    m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points));
 | 
			
		||||
| 
						 | 
				
			
			@ -1961,7 +1965,7 @@ namespace Slic3r {
 | 
			
		|||
        for (const ModelObject* object : model.objects)
 | 
			
		||||
        {
 | 
			
		||||
            ++count;
 | 
			
		||||
            const std::vector<Vec3f>& sla_support_points = object->sla_support_points;
 | 
			
		||||
            const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
 | 
			
		||||
            if (!sla_support_points.empty())
 | 
			
		||||
            {
 | 
			
		||||
                sprintf(buffer, "object_id=%d|", count);
 | 
			
		||||
| 
						 | 
				
			
			@ -1970,7 +1974,7 @@ namespace Slic3r {
 | 
			
		|||
                // Store the layer height profile as a single space separated list.
 | 
			
		||||
                for (size_t i = 0; i < sla_support_points.size(); ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    sprintf(buffer, (i==0 ? "%f %f %f" : " %f %f %f"),  sla_support_points[i](0), sla_support_points[i](1), sla_support_points[i](2));
 | 
			
		||||
                    sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"),  sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)sla_support_points[i].is_new_island);
 | 
			
		||||
                    out += buffer;
 | 
			
		||||
                }
 | 
			
		||||
                out += "\n";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -583,7 +583,7 @@ void AMFParserContext::endElement(const char * /* name */)
 | 
			
		|||
            else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) {
 | 
			
		||||
                // Parse object's layer height profile, a semicolon separated list of floats.
 | 
			
		||||
                unsigned char coord_idx = 0;
 | 
			
		||||
                Vec3f point(Vec3f::Zero());
 | 
			
		||||
                Eigen::Matrix<float, 5, 1, Eigen::DontAlign> point(Eigen::Matrix<float, 5, 1, Eigen::DontAlign>::Zero());
 | 
			
		||||
                char *p = const_cast<char*>(m_value[1].c_str());
 | 
			
		||||
                for (;;) {
 | 
			
		||||
                    char *end = strchr(p, ';');
 | 
			
		||||
| 
						 | 
				
			
			@ -591,8 +591,8 @@ void AMFParserContext::endElement(const char * /* name */)
 | 
			
		|||
	                    *end = 0;
 | 
			
		||||
 | 
			
		||||
                    point(coord_idx) = atof(p);
 | 
			
		||||
                    if (++coord_idx == 3) {
 | 
			
		||||
                        m_object->sla_support_points.push_back(point);
 | 
			
		||||
                    if (++coord_idx == 5) {
 | 
			
		||||
                        m_object->sla_support_points.push_back(sla::SupportPoint(point));
 | 
			
		||||
                        coord_idx = 0;
 | 
			
		||||
                    }
 | 
			
		||||
					if (end == nullptr)
 | 
			
		||||
| 
						 | 
				
			
			@ -900,14 +900,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
 | 
			
		|||
        }
 | 
			
		||||
        //FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
 | 
			
		||||
 | 
			
		||||
        const std::vector<Vec3f>& sla_support_points = object->sla_support_points;
 | 
			
		||||
        const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
 | 
			
		||||
        if (!sla_support_points.empty()) {
 | 
			
		||||
            // Store the SLA supports as a single semicolon separated list.
 | 
			
		||||
            stream << "    <metadata type=\"slic3r.sla_support_points\">";
 | 
			
		||||
            for (size_t i = 0; i < sla_support_points.size(); ++i) {
 | 
			
		||||
                if (i != 0)
 | 
			
		||||
                    stream << ";";
 | 
			
		||||
                stream << sla_support_points[i](0) << ";" << sla_support_points[i](1) << ";" << sla_support_points[i](2);
 | 
			
		||||
                stream << sla_support_points[i].pos(0) << ";" << sla_support_points[i].pos(1) << ";" << sla_support_points[i].pos(2) << ";" << sla_support_points[i].head_front_radius << ";" << sla_support_points[i].is_new_island;
 | 
			
		||||
            }
 | 
			
		||||
            stream << "\n    </metadata>\n";
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@
 | 
			
		|||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "Geometry.hpp"
 | 
			
		||||
#include <libslic3r/SLA/SLASupportTree.hpp>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +176,8 @@ public:
 | 
			
		|||
 | 
			
		||||
    // This vector holds position of selected support points for SLA. The data are
 | 
			
		||||
    // saved in mesh coordinates to allow using them for several instances.
 | 
			
		||||
    std::vector<Vec3f>      sla_support_points;
 | 
			
		||||
    // The format is (x, y, z, point_size, supports_island)
 | 
			
		||||
    std::vector<sla::SupportPoint>      sla_support_points;
 | 
			
		||||
 | 
			
		||||
    /* This vector accumulates the total translation applied to the object by the
 | 
			
		||||
        center_around_origin() method. Callers might want to apply the same translation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,34 +13,7 @@
 | 
			
		|||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices, const std::vector<float>& heights, 
 | 
			
		||||
    const Config& config, std::function<void(void)> throw_on_cancel)
 | 
			
		||||
: m_config(config), m_V(emesh.V), m_F(emesh.F), m_throw_on_cancel(throw_on_cancel)
 | 
			
		||||
{
 | 
			
		||||
    // FIXME: It might be safer to get rid of the rand() calls altogether, because it is probably
 | 
			
		||||
    // not always thread-safe and can be slow if it is.
 | 
			
		||||
    srand(time(NULL)); // rand() is used by igl::random_point_on_mesh
 | 
			
		||||
 | 
			
		||||
    // Find all separate islands that will need support. The coord_t number denotes height
 | 
			
		||||
    // of a point just below the mesh (so that we can later project the point precisely
 | 
			
		||||
    // on the mesh by raycasting (done by igl) and not risking we will place the point inside).
 | 
			
		||||
    std::vector<std::pair<ExPolygon, coord_t>> islands = find_islands(slices, heights);
 | 
			
		||||
 | 
			
		||||
    // Uniformly cover each of the islands with support points.
 | 
			
		||||
    for (const auto& island : islands) {
 | 
			
		||||
        std::vector<Vec3d> points = uniformly_cover(island);
 | 
			
		||||
        m_throw_on_cancel();
 | 
			
		||||
        project_upward_onto_mesh(points);
 | 
			
		||||
        m_output.insert(m_output.end(), points.begin(), points.end());
 | 
			
		||||
        m_throw_on_cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We are done with the islands. Let's sprinkle the rest of the mesh.
 | 
			
		||||
    // The function appends to m_output.
 | 
			
		||||
    sprinkle_mesh(mesh);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2)
 | 
			
		||||
{
 | 
			
		||||
    n1.normalize();
 | 
			
		||||
| 
						 | 
				
			
			@ -59,115 +32,6 @@ float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::sprinkle_mesh(const TriangleMesh& mesh)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<Vec3d> points;
 | 
			
		||||
    // Loads the ModelObject raw_mesh and transforms it by first instance's transformation matrix (disregarding translation).
 | 
			
		||||
    // Instances only differ in z-rotation, so it does not matter which of them will be used for the calculation.
 | 
			
		||||
    // The supports point will be calculated on this mesh (so scaling ang vertical direction is correctly accounted for).
 | 
			
		||||
    // Results will be inverse-transformed to raw_mesh coordinates.
 | 
			
		||||
    //TriangleMesh mesh = m_model_object.raw_mesh();
 | 
			
		||||
    //Transform3d transformation_matrix = m_model_object.instances[0]->get_matrix(true/*dont_translate*/);
 | 
			
		||||
    //mesh.transform(transformation_matrix);
 | 
			
		||||
 | 
			
		||||
    // Check that the object is thick enough to produce any support points
 | 
			
		||||
    BoundingBoxf3 bb = mesh.bounding_box();
 | 
			
		||||
    if (bb.size()(2) < m_config.minimal_z)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    // All points that we curretly have must be transformed too, so distance to them is correcly calculated.
 | 
			
		||||
    //for (Vec3f& point : m_model_object.sla_support_points)
 | 
			
		||||
    //    point = transformation_matrix.cast<float>() * point;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // In order to calculate distance to already placed points, we must keep know which facet the point lies on.
 | 
			
		||||
    std::vector<Vec3d> facets_normals;
 | 
			
		||||
 | 
			
		||||
    // Only points belonging to islands were added so far - they all lie on horizontal surfaces:
 | 
			
		||||
    for (unsigned int i=0; i<m_output.size(); ++i)
 | 
			
		||||
        facets_normals.push_back(Vec3d(0,0,-1));
 | 
			
		||||
 | 
			
		||||
    // The AABB hierarchy will be used to find normals of already placed points.
 | 
			
		||||
    // The points added automatically will just push_back the new normal on the fly.
 | 
			
		||||
    /*igl::AABB<Eigen::MatrixXf,3> aabb;
 | 
			
		||||
    aabb.init(V, F);
 | 
			
		||||
    for (unsigned int i=0; i<m_model_object.sla_support_points.size(); ++i) {
 | 
			
		||||
        int facet_idx = 0;
 | 
			
		||||
        Eigen::Matrix<float, 1, 3> dump;
 | 
			
		||||
        Eigen::MatrixXf query_point = m_model_object.sla_support_points[i];
 | 
			
		||||
        aabb.squared_distance(V, F, query_point, facet_idx, dump);
 | 
			
		||||
        Vec3f a1 = V.row(F(facet_idx,1)) - V.row(F(facet_idx,0));
 | 
			
		||||
        Vec3f a2 = V.row(F(facet_idx,2)) - V.row(F(facet_idx,0));
 | 
			
		||||
        Vec3f normal = a1.cross(a2);
 | 
			
		||||
        normal.normalize();
 | 
			
		||||
        facets_normals.push_back(normal);
 | 
			
		||||
    }*/
 | 
			
		||||
 | 
			
		||||
    // New potential support point is randomly generated on the mesh and distance to all already placed points is calculated.
 | 
			
		||||
    // In case it is never smaller than certain limit (depends on the new point's facet normal), the point is accepted.
 | 
			
		||||
    // The process stops after certain number of points is refused in a row.
 | 
			
		||||
    Vec3d point;
 | 
			
		||||
    Vec3d normal;
 | 
			
		||||
    int added_points = 0;
 | 
			
		||||
    int refused_points = 0;
 | 
			
		||||
    const int refused_limit = 30;
 | 
			
		||||
    // Angle at which the density reaches zero:
 | 
			
		||||
    const float threshold_angle = std::min(M_PI_2, M_PI_4 * acos(0.f/m_config.density_at_horizontal) / acos(m_config.density_at_45/m_config.density_at_horizontal));
 | 
			
		||||
 | 
			
		||||
    size_t cancel_test_cntr = 0;
 | 
			
		||||
    while (refused_points < refused_limit) {
 | 
			
		||||
        if (++ cancel_test_cntr == 500) {
 | 
			
		||||
            // Don't call the cancellation routine too often as the multi-core cache synchronization
 | 
			
		||||
            // may be pretty expensive.
 | 
			
		||||
            m_throw_on_cancel();
 | 
			
		||||
            cancel_test_cntr = 0;
 | 
			
		||||
        }
 | 
			
		||||
        // Place a random point on the mesh and calculate corresponding facet's normal:
 | 
			
		||||
        Eigen::VectorXi FI;
 | 
			
		||||
        Eigen::MatrixXd B;
 | 
			
		||||
        igl::random_points_on_mesh(1, m_V, m_F, B, FI);
 | 
			
		||||
        point = B(0,0)*m_V.row(m_F(FI(0),0)) +
 | 
			
		||||
                B(0,1)*m_V.row(m_F(FI(0),1)) +
 | 
			
		||||
                B(0,2)*m_V.row(m_F(FI(0),2));
 | 
			
		||||
        if (point(2) - bb.min(2) < m_config.minimal_z)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        Vec3d a1 = m_V.row(m_F(FI(0),1)) - m_V.row(m_F(FI(0),0));
 | 
			
		||||
        Vec3d a2 = m_V.row(m_F(FI(0),2)) - m_V.row(m_F(FI(0),0));
 | 
			
		||||
        normal = a1.cross(a2);
 | 
			
		||||
        normal.normalize();
 | 
			
		||||
 | 
			
		||||
        // calculate angle between the normal and vertical:
 | 
			
		||||
        float angle = angle_from_normal(normal.cast<float>());
 | 
			
		||||
        if (angle > threshold_angle)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        const float limit = distance_limit(angle);
 | 
			
		||||
        bool add_it = true;
 | 
			
		||||
        for (unsigned int i=0; i<points.size(); ++i) {
 | 
			
		||||
            if (approximate_geodesic_distance(points[i], point, facets_normals[i], normal) < limit) {
 | 
			
		||||
                add_it = false;
 | 
			
		||||
                ++refused_points;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (add_it) {
 | 
			
		||||
            points.push_back(point.cast<double>());
 | 
			
		||||
            facets_normals.push_back(normal);
 | 
			
		||||
            ++added_points;
 | 
			
		||||
            refused_points = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    m_output.insert(m_output.end(), points.begin(), points.end());
 | 
			
		||||
 | 
			
		||||
    // Now transform all support points to mesh coordinates:
 | 
			
		||||
    //for (Vec3f& point : m_model_object.sla_support_points)
 | 
			
		||||
    //    point = transformation_matrix.inverse().cast<float>() * point;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
float SLAAutoSupports::get_required_density(float angle) const
 | 
			
		||||
{
 | 
			
		||||
    // calculation would be density_0 * cos(angle). To provide one more degree of freedom, we will scale the angle
 | 
			
		||||
| 
						 | 
				
			
			@ -179,9 +43,240 @@ float SLAAutoSupports::get_required_density(float angle) const
 | 
			
		|||
float SLAAutoSupports::distance_limit(float angle) const
 | 
			
		||||
{
 | 
			
		||||
    return 1./(2.4*get_required_density(angle));
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices, const std::vector<float>& heights,
 | 
			
		||||
                                   const Config& config, std::function<void(void)> throw_on_cancel)
 | 
			
		||||
: m_config(config), m_V(emesh.V), m_F(emesh.F), m_throw_on_cancel(throw_on_cancel)
 | 
			
		||||
{
 | 
			
		||||
    // Find all separate islands that will need support. The coord_t number denotes height
 | 
			
		||||
    // of a point just below the mesh (so that we can later project the point precisely
 | 
			
		||||
    // on the mesh by raycasting (done by igl) and not risking we will place the point inside).
 | 
			
		||||
    /*std::vector<std::pair<ExPolygon, coord_t>> islands = */
 | 
			
		||||
    process(slices, heights);
 | 
			
		||||
 | 
			
		||||
    // Uniformly cover each of the islands with support points.
 | 
			
		||||
    /*for (const auto& island : islands) {
 | 
			
		||||
        std::vector<Vec3d> points = uniformly_cover(island);
 | 
			
		||||
        m_throw_on_cancel();
 | 
			
		||||
        project_upward_onto_mesh(points);
 | 
			
		||||
        m_output.insert(m_output.end(), points.begin(), points.end());
 | 
			
		||||
        m_throw_on_cancel();
 | 
			
		||||
    }*/
 | 
			
		||||
    project_onto_mesh(m_output);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::project_onto_mesh(std::vector<sla::SupportPoint>& points) const
 | 
			
		||||
{
 | 
			
		||||
    // The function  makes sure that all the points are really exactly placed on the mesh.
 | 
			
		||||
    igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f};
 | 
			
		||||
    igl::Hit hit_down{0, 0, 0.f, 0.f, 0.f};
 | 
			
		||||
    
 | 
			
		||||
    for (sla::SupportPoint& support_point : points) {
 | 
			
		||||
        Vec3f& p = support_point.pos;
 | 
			
		||||
        // Project the point upward and downward and choose the closer intersection with the mesh.
 | 
			
		||||
        bool up   = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., 1.), m_V, m_F, hit_up);
 | 
			
		||||
        bool down = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., -1.), m_V, m_F, hit_down);
 | 
			
		||||
 | 
			
		||||
        if (!up && !down)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        igl::Hit& hit = (!down || (hit_up.t < hit_down.t)) ? hit_up : hit_down;
 | 
			
		||||
        int fid = hit.id;
 | 
			
		||||
        Vec3f bc(1-hit.u-hit.v, hit.u, hit.v);
 | 
			
		||||
        p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast<float>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<std::pair<ExPolygon, coord_t>> islands;
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i = 0; i<slices.size(); ++i) {
 | 
			
		||||
        const ExPolygons& expolys_top = slices[i];
 | 
			
		||||
 | 
			
		||||
        const float height = (i>2 ? heights[i-3] : heights[0]-(heights[1]-heights[0]));
 | 
			
		||||
        
 | 
			
		||||
        const float safe_angle = 5.f * (M_PI/180.f); // smaller number - less supports
 | 
			
		||||
        const float offset =  scale_((i!=0 ? heights[i]-heights[i-1] : heights[0]) / std::tan(safe_angle));
 | 
			
		||||
        const float pixel_area = 0.047f * 0.047f; // FIXME: calculate actual pixel area from printer config
 | 
			
		||||
 | 
			
		||||
        // Check all ExPolygons on this slice and check whether they are new or belonging to something below.
 | 
			
		||||
        for (const ExPolygon& polygon : expolys_top) {
 | 
			
		||||
            if (polygon.area() * SCALING_FACTOR * SCALING_FACTOR < pixel_area)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            m_structures_new.emplace_back(polygon, height);
 | 
			
		||||
            for (Structure& s : m_structures_old) {
 | 
			
		||||
                const ExPolygon* bottom = s.polygon;
 | 
			
		||||
                if (polygon.overlaps(*bottom) || bottom->overlaps(polygon)) {
 | 
			
		||||
                    m_structures_new.back().structures_below.push_back(&s);
 | 
			
		||||
                    coord_t centroids_dist = (bottom->contour.centroid() - polygon.contour.centroid()).norm();
 | 
			
		||||
                    if (centroids_dist != 0) {
 | 
			
		||||
                        float mult = std::min(1.f, 1.f - std::min(1.f, 500.f * (float)(centroids_dist * centroids_dist) / (float)bottom->area()));
 | 
			
		||||
                        s.supports_force *= mult;
 | 
			
		||||
                    }
 | 
			
		||||
                    //s.supports_force *= std::min(1.f, ((float)polygon.area()/(float)bottom->area()));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Let's assign proper support force to each of them:
 | 
			
		||||
        for (Structure& old_str : m_structures_old) {
 | 
			
		||||
            std::vector<Structure*> children;
 | 
			
		||||
            float children_area = 0.f;
 | 
			
		||||
            for (Structure& new_str : m_structures_new)
 | 
			
		||||
                for (const Structure* below : new_str.structures_below)
 | 
			
		||||
                    if (&old_str == below) {
 | 
			
		||||
                        children.push_back(&new_str);
 | 
			
		||||
                        children_area += children.back()->polygon->area() * pow(SCALING_FACTOR, 2);
 | 
			
		||||
                    }
 | 
			
		||||
            for (Structure* child : children)
 | 
			
		||||
                child->supports_force += (old_str.supports_force/children_area) * (child->polygon->area() * pow(SCALING_FACTOR, 2));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Now iterate over all polygons and append new points if needed.
 | 
			
		||||
        for (Structure& s : m_structures_new) {
 | 
			
		||||
            if (s.structures_below.empty()) {// completely new island - needs support no doubt
 | 
			
		||||
                uniformly_cover(*s.polygon, s, true);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Let's see if there's anything that overlaps enough to need supports:
 | 
			
		||||
                ExPolygons polygons;
 | 
			
		||||
                float bottom_area = 0.f;
 | 
			
		||||
                for (const Structure* sb : s.structures_below) {
 | 
			
		||||
                    polygons.push_back(*sb->polygon);
 | 
			
		||||
                    bottom_area += polygons.back().area() * pow(SCALING_FACTOR, 2);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                polygons = offset_ex(polygons, offset);
 | 
			
		||||
                polygons = diff_ex(ExPolygons{*s.polygon}, polygons);
 | 
			
		||||
                
 | 
			
		||||
                // What we now have in polygons needs support, regardless of what the forces are, so we can add them.
 | 
			
		||||
                for (const ExPolygon& p : polygons)
 | 
			
		||||
                    uniformly_cover(p, s);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We should also check if current support is enough given the polygon area.
 | 
			
		||||
        for (Structure& s : m_structures_new) {
 | 
			
		||||
            ExPolygons e;
 | 
			
		||||
            float e_area = 0.f;
 | 
			
		||||
            for (const Structure* a : s.structures_below) {
 | 
			
		||||
                e.push_back(*a->polygon);
 | 
			
		||||
                e_area += e.back().area() * SCALING_FACTOR * SCALING_FACTOR;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            e = diff_ex(ExPolygons{*s.polygon}, e);
 | 
			
		||||
            s.supports_force /= std::max(1., (e_area / (s.polygon->area()*SCALING_FACTOR*SCALING_FACTOR)));
 | 
			
		||||
                
 | 
			
		||||
                
 | 
			
		||||
                
 | 
			
		||||
            if ( (s.polygon->area() * pow(SCALING_FACTOR, 2)) * m_config.tear_pressure > s.supports_force) {
 | 
			
		||||
                ExPolygons::iterator largest_it = std::max_element(e.begin(), e.end(), [](const ExPolygon& a, const ExPolygon& b) { return a.area() < b.area(); });
 | 
			
		||||
                if (!e.empty())
 | 
			
		||||
                    uniformly_cover(*largest_it, s);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // All is done. Prepare to advance to the next layer. 
 | 
			
		||||
        m_structures_old = m_structures_new;
 | 
			
		||||
        m_structures_new.clear();
 | 
			
		||||
 | 
			
		||||
        m_throw_on_cancel();
 | 
			
		||||
 | 
			
		||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
 | 
			
		||||
        /*std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i);
 | 
			
		||||
        output_expolygons(expolys_top, "top" + layer_num_str + ".svg");
 | 
			
		||||
        output_expolygons(diff, "diff" + layer_num_str + ".svg");
 | 
			
		||||
        if (!islands.empty())
 | 
			
		||||
            output_expolygons(islands, "islands" + layer_num_str + ".svg");*/
 | 
			
		||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::add_point(const Point& point, Structure& structure, bool is_new_island)
 | 
			
		||||
{
 | 
			
		||||
    sla::SupportPoint new_point(point(0) * SCALING_FACTOR, point(1) * SCALING_FACTOR, structure.height, 0.4f, (float)is_new_island);
 | 
			
		||||
    
 | 
			
		||||
    m_output.emplace_back(new_point);
 | 
			
		||||
    structure.supports_force += m_config.support_force;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& structure, bool is_new_island, bool just_one)
 | 
			
		||||
{
 | 
			
		||||
    //int num_of_points = std::max(1, (int)((island.area()*pow(SCALING_FACTOR, 2) * m_config.tear_pressure)/m_config.support_force));
 | 
			
		||||
    
 | 
			
		||||
    const float density_horizontal = m_config.tear_pressure / m_config.support_force;
 | 
			
		||||
 | 
			
		||||
    // We will cover the island another way.
 | 
			
		||||
    // For now we'll just place the points randomly not too close to the others.
 | 
			
		||||
    std::random_device rd;
 | 
			
		||||
    std::mt19937 gen(rd());
 | 
			
		||||
    std::uniform_real_distribution<> dis(0., 1.);
 | 
			
		||||
 | 
			
		||||
    std::vector<Vec3d> island_new_points;
 | 
			
		||||
    const BoundingBox& bb = get_extents(island);
 | 
			
		||||
    const int refused_limit = 30;
 | 
			
		||||
    int refused_points = 0;
 | 
			
		||||
    while (refused_points < refused_limit) {
 | 
			
		||||
        Point out;
 | 
			
		||||
        if (refused_points == 0 && island_new_points.empty()) // first iteration
 | 
			
		||||
            out = island.contour.centroid();
 | 
			
		||||
        else
 | 
			
		||||
            out = Point(bb.min(0) + bb.size()(0)  * dis(gen), bb.min(1) + bb.size()(1)  * dis(gen));
 | 
			
		||||
 | 
			
		||||
        Vec3d unscaled_out = unscale(out(0), out(1), 0.f);
 | 
			
		||||
        bool add_it = true;
 | 
			
		||||
 | 
			
		||||
        if (!island.contour.contains(out))
 | 
			
		||||
            add_it = false;
 | 
			
		||||
        else
 | 
			
		||||
            for (const Polygon& hole : island.holes)
 | 
			
		||||
                if (hole.contains(out))
 | 
			
		||||
                    add_it = false;
 | 
			
		||||
 | 
			
		||||
        if (add_it) {
 | 
			
		||||
            for (const Vec3d& p : island_new_points) {
 | 
			
		||||
                if ((p - unscaled_out).squaredNorm() < 1./(2.4*density_horizontal))    {
 | 
			
		||||
                    add_it = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (add_it) {
 | 
			
		||||
            island_new_points.emplace_back(unscaled_out);
 | 
			
		||||
            if (just_one)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            ++refused_points;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const Vec3d& p : island_new_points)
 | 
			
		||||
        add_point(Point(scale_(p.x()), scale_(p.y())), structure, is_new_island);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
 | 
			
		||||
void SLAAutoSupports::output_structures() const
 | 
			
		||||
{
 | 
			
		||||
    const std::vector<Structure>& structures = m_structures_new;
 | 
			
		||||
    for (unsigned int i=0 ; i<structures.size(); ++i) {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << structures[i].unique_id.count() << "_" << std::setw(10) << std::setfill('0') << 1000 + (int)structures[i].height/1000 << ".png";
 | 
			
		||||
        output_expolygons(std::vector<ExPolygon>{*structures[i].polygon}, ss.str());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string filename) const
 | 
			
		||||
{
 | 
			
		||||
    BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000));
 | 
			
		||||
| 
						 | 
				
			
			@ -198,138 +293,6 @@ void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string f
 | 
			
		|||
        svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
 | 
			
		||||
 | 
			
		||||
std::vector<std::pair<ExPolygon, coord_t>> SLAAutoSupports::find_islands(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) const
 | 
			
		||||
{
 | 
			
		||||
    std::vector<std::pair<ExPolygon, coord_t>> islands;
 | 
			
		||||
 | 
			
		||||
    struct PointAccessor {
 | 
			
		||||
        const Point* operator()(const Point &pt) const { return &pt; }
 | 
			
		||||
    };
 | 
			
		||||
    typedef ClosestPointInRadiusLookup<Point, PointAccessor> ClosestPointLookupType;
 | 
			
		||||
 | 
			
		||||
    for (unsigned int i = 0; i<slices.size(); ++i) {
 | 
			
		||||
        const ExPolygons& expolys_top = slices[i];
 | 
			
		||||
        const ExPolygons& expolys_bottom = (i == 0 ? ExPolygons() : slices[i-1]);
 | 
			
		||||
 | 
			
		||||
        std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i);
 | 
			
		||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
 | 
			
		||||
        output_expolygons(expolys_top, "top" + layer_num_str + ".svg");
 | 
			
		||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
 | 
			
		||||
        ExPolygons diff = diff_ex(expolys_top, expolys_bottom);
 | 
			
		||||
 | 
			
		||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
 | 
			
		||||
        output_expolygons(diff, "diff" + layer_num_str + ".svg");
 | 
			
		||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
 | 
			
		||||
 | 
			
		||||
        ClosestPointLookupType cpl(SCALED_EPSILON);
 | 
			
		||||
        for (const ExPolygon& expol : expolys_top) {
 | 
			
		||||
            for (const Point& p : expol.contour.points)
 | 
			
		||||
                cpl.insert(p);
 | 
			
		||||
            for (const Polygon& hole : expol.holes)
 | 
			
		||||
                for (const Point& p : hole.points)
 | 
			
		||||
                    cpl.insert(p);
 | 
			
		||||
            // the lookup structure now contains all points from the top slice
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (const ExPolygon& polygon : diff) {
 | 
			
		||||
            // we want to check all boundary points of the diff polygon
 | 
			
		||||
            bool island = true;
 | 
			
		||||
            for (const Point& p : polygon.contour.points) {
 | 
			
		||||
                if (cpl.find(p).second != 0) { // the point belongs to the bottom slice - this cannot be an island
 | 
			
		||||
                    island = false;
 | 
			
		||||
                    goto NO_ISLAND;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (const Polygon& hole : polygon.holes)
 | 
			
		||||
                for (const Point& p : hole.points)
 | 
			
		||||
                if (cpl.find(p).second != 0) {
 | 
			
		||||
                    island = false;
 | 
			
		||||
                    goto NO_ISLAND;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            if (island) { // all points of the diff polygon are from the top slice
 | 
			
		||||
                islands.push_back(std::make_pair(polygon, scale_(i!=0 ? heights[i-1] : heights[0]-(heights[1]-heights[0]))));
 | 
			
		||||
            }
 | 
			
		||||
            NO_ISLAND: ;// continue with next ExPolygon
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
 | 
			
		||||
        //if (!islands.empty())
 | 
			
		||||
          //  output_expolygons(islands, "islands" + layer_num_str + ".svg");
 | 
			
		||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
 | 
			
		||||
        m_throw_on_cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return islands;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3d> SLAAutoSupports::uniformly_cover(const std::pair<ExPolygon, coord_t>& island)
 | 
			
		||||
{
 | 
			
		||||
    int num_of_points = std::max(1, (int)(island.first.area()*pow(SCALING_FACTOR, 2) * get_required_density(0)));
 | 
			
		||||
 | 
			
		||||
    // In case there is just one point to place, we'll place it into the polygon's centroid (unless it lies in a hole).
 | 
			
		||||
    if (num_of_points == 1) {
 | 
			
		||||
        Point out(island.first.contour.centroid());
 | 
			
		||||
 | 
			
		||||
        for (const auto& hole : island.first.holes)
 | 
			
		||||
            if (hole.contains(out))
 | 
			
		||||
                goto HOLE_HIT;
 | 
			
		||||
        return std::vector<Vec3d>{unscale(out(0), out(1), island.second)};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
HOLE_HIT:
 | 
			
		||||
    // In this case either the centroid lies in a hole, or there are multiple points
 | 
			
		||||
    // to place. We will cover the island another way.
 | 
			
		||||
    // For now we'll just place the points randomly not too close to the others.
 | 
			
		||||
    std::random_device rd;
 | 
			
		||||
    std::mt19937 gen(rd());
 | 
			
		||||
    std::uniform_real_distribution<> dis(0., 1.);
 | 
			
		||||
 | 
			
		||||
    std::vector<Vec3d> island_new_points;
 | 
			
		||||
    const BoundingBox& bb = get_extents(island.first);
 | 
			
		||||
    const int refused_limit = 30;
 | 
			
		||||
    int refused_points = 0;
 | 
			
		||||
    while (refused_points < refused_limit) {
 | 
			
		||||
        Point out(bb.min(0) + bb.size()(0)  * dis(gen),
 | 
			
		||||
                  bb.min(1) + bb.size()(1)  * dis(gen)) ;
 | 
			
		||||
        Vec3d unscaled_out = unscale(out(0), out(1), island.second);
 | 
			
		||||
        bool add_it = true;
 | 
			
		||||
 | 
			
		||||
        if (!island.first.contour.contains(out))
 | 
			
		||||
            add_it = false;
 | 
			
		||||
        else
 | 
			
		||||
            for (const Polygon& hole : island.first.holes)
 | 
			
		||||
                if (hole.contains(out))
 | 
			
		||||
                    add_it = false;
 | 
			
		||||
 | 
			
		||||
        if (add_it) {
 | 
			
		||||
            for (const Vec3d& p : island_new_points) {
 | 
			
		||||
                if ((p - unscaled_out).squaredNorm() < distance_limit(0)) {
 | 
			
		||||
                    add_it = false;
 | 
			
		||||
                    ++refused_points;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (add_it)
 | 
			
		||||
            island_new_points.emplace_back(unscaled_out);
 | 
			
		||||
    }
 | 
			
		||||
    return island_new_points;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SLAAutoSupports::project_upward_onto_mesh(std::vector<Vec3d>& points) const
 | 
			
		||||
{
 | 
			
		||||
    Vec3f dir(0., 0., 1.);
 | 
			
		||||
    igl::Hit hit{0, 0, 0.f, 0.f, 0.f};
 | 
			
		||||
    for (Vec3d& p : points) {
 | 
			
		||||
        igl::ray_mesh_intersect(p.cast<float>(), dir, m_V, m_F, hit);
 | 
			
		||||
        int fid = hit.id;
 | 
			
		||||
        Vec3f bc(1-hit.u-hit.v, hit.u, hit.v);
 | 
			
		||||
        p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast<double>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
| 
						 | 
				
			
			@ -15,30 +15,45 @@ public:
 | 
			
		|||
            float density_at_horizontal;
 | 
			
		||||
            float density_at_45;
 | 
			
		||||
            float minimal_z;
 | 
			
		||||
            ///////////////
 | 
			
		||||
            float support_force = 30; // a force one point can support       (arbitrary force unit)
 | 
			
		||||
            float tear_pressure = 1; // pressure that the display exerts    (the force unit per mm2)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices, 
 | 
			
		||||
        const std::vector<float>& heights, const Config& config, std::function<void(void)> throw_on_cancel);
 | 
			
		||||
    const std::vector<Vec3d>& output() { return m_output; }
 | 
			
		||||
    SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices,
 | 
			
		||||
                     const std::vector<float>& heights, const Config& config, std::function<void(void)> throw_on_cancel);
 | 
			
		||||
    const std::vector<sla::SupportPoint>& output() { return m_output; }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<Vec3d> m_output;
 | 
			
		||||
    std::vector<Vec3d> m_normals;
 | 
			
		||||
    TriangleMesh mesh;
 | 
			
		||||
    static float angle_from_normal(const stl_normal& normal) { return acos((-normal.normalized())(2)); }
 | 
			
		||||
    float get_required_density(float angle) const;
 | 
			
		||||
    float distance_limit(float angle) const;
 | 
			
		||||
    static float approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2);
 | 
			
		||||
    std::vector<std::pair<ExPolygon, coord_t>> find_islands(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) const;
 | 
			
		||||
    void sprinkle_mesh(const TriangleMesh& mesh);
 | 
			
		||||
    std::vector<Vec3d> uniformly_cover(const std::pair<ExPolygon, coord_t>& island);
 | 
			
		||||
    void project_upward_onto_mesh(std::vector<Vec3d>& points) const;
 | 
			
		||||
    std::vector<sla::SupportPoint> m_output;
 | 
			
		||||
 | 
			
		||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
 | 
			
		||||
    void output_expolygons(const ExPolygons& expolys, std::string filename) const;
 | 
			
		||||
    void output_structures() const;
 | 
			
		||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
 | 
			
		||||
 | 
			
		||||
    SLAAutoSupports::Config m_config;
 | 
			
		||||
 | 
			
		||||
    struct Structure {
 | 
			
		||||
        Structure(const ExPolygon& poly, float h) : height(h), unique_id(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())) {
 | 
			
		||||
            polygon = &poly;
 | 
			
		||||
        }
 | 
			
		||||
        const ExPolygon* polygon = nullptr;
 | 
			
		||||
        std::vector<const Structure*> structures_below;
 | 
			
		||||
        float height = 0;
 | 
			
		||||
        float supports_force = 0.f;
 | 
			
		||||
        std::chrono::milliseconds unique_id;
 | 
			
		||||
    };
 | 
			
		||||
    std::vector<Structure> m_structures_old;
 | 
			
		||||
    std::vector<Structure> m_structures_new;
 | 
			
		||||
    float m_supports_force_total = 0.f;
 | 
			
		||||
 | 
			
		||||
    void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights);
 | 
			
		||||
    void add_point(const Point& point, Structure& structure, bool island  = false);
 | 
			
		||||
    void uniformly_cover(const ExPolygon& island, Structure& structure, bool is_new_island = false, bool just_one = false);
 | 
			
		||||
    void project_onto_mesh(std::vector<sla::SupportPoint>& points) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::function<void(void)> m_throw_on_cancel;
 | 
			
		||||
    const Eigen::MatrixXd& m_V;
 | 
			
		||||
    const Eigen::MatrixXi& m_F;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -607,10 +607,18 @@ EigenMesh3D to_eigenmesh(const ModelObject& modelobj) {
 | 
			
		|||
    return to_eigenmesh(modelobj.raw_mesh());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PointSet to_point_set(const std::vector<Vec3d> &v)
 | 
			
		||||
PointSet to_point_set(const std::vector<SupportPoint> &v)
 | 
			
		||||
{
 | 
			
		||||
    PointSet ret(v.size(), 3);
 | 
			
		||||
    { long i = 0; for(const Vec3d& p : v) ret.row(i++) = p; }
 | 
			
		||||
    PointSet ret(v.size(), 5);
 | 
			
		||||
    long i = 0;
 | 
			
		||||
    for(const SupportPoint& support_point : v) {
 | 
			
		||||
        ret.row(i)(0) = support_point.pos(0);
 | 
			
		||||
        ret.row(i)(1) = support_point.pos(1);
 | 
			
		||||
        ret.row(i)(2) = support_point.pos(2);
 | 
			
		||||
        ret.row(i)(3) = support_point.head_front_radius;
 | 
			
		||||
        ret.row(i)(4) = (float)support_point.is_new_island;
 | 
			
		||||
        ++i;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,26 @@ enum class PillarConnectionMode {
 | 
			
		|||
    dynamic
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SupportPoint {
 | 
			
		||||
    Vec3f pos;
 | 
			
		||||
    float head_front_radius;
 | 
			
		||||
    bool is_new_island;
 | 
			
		||||
 | 
			
		||||
    SupportPoint() :
 | 
			
		||||
    pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) {}
 | 
			
		||||
 | 
			
		||||
    SupportPoint(float pos_x, float pos_y, float pos_z, float head_radius, bool new_island) :
 | 
			
		||||
    pos(pos_x, pos_y, pos_z), head_front_radius(head_radius), is_new_island(new_island) {}
 | 
			
		||||
 | 
			
		||||
    SupportPoint(Vec3f position, float head_radius, bool new_island) :
 | 
			
		||||
    pos(position), head_front_radius(head_radius), is_new_island(new_island) {}
 | 
			
		||||
 | 
			
		||||
    SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data) :
 | 
			
		||||
    pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4)) {}
 | 
			
		||||
 | 
			
		||||
    bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct SupportConfig {
 | 
			
		||||
    // Radius in mm of the pointing side of the head.
 | 
			
		||||
    double head_front_radius_mm = 0.2;
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +138,7 @@ EigenMesh3D to_eigenmesh(const TriangleMesh& m);
 | 
			
		|||
EigenMesh3D to_eigenmesh(const ModelObject& model);
 | 
			
		||||
 | 
			
		||||
// Simple conversion of 'vector of points' to an Eigen matrix
 | 
			
		||||
PointSet    to_point_set(const std::vector<Vec3d>&);
 | 
			
		||||
PointSet    to_point_set(const std::vector<sla::SupportPoint>&);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************** */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -541,7 +541,7 @@ void SLAPrint::process()
 | 
			
		|||
                                          [this]() { throw_if_canceled(); });
 | 
			
		||||
 | 
			
		||||
            // Now let's extract the result.
 | 
			
		||||
            const std::vector<Vec3d>& points = auto_supports.output();
 | 
			
		||||
            const std::vector<sla::SupportPoint>& points = auto_supports.output();
 | 
			
		||||
            this->throw_if_canceled();
 | 
			
		||||
            po.m_supportdata->support_points = sla::to_point_set(points);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1236,15 +1236,19 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const {
 | 
			
		|||
    return m_transformed_rmesh.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3d> SLAPrintObject::transformed_support_points() const
 | 
			
		||||
std::vector<sla::SupportPoint> SLAPrintObject::transformed_support_points() const
 | 
			
		||||
{
 | 
			
		||||
    assert(m_model_object != nullptr);
 | 
			
		||||
    auto& spts = m_model_object->sla_support_points;
 | 
			
		||||
    std::vector<sla::SupportPoint>& spts = m_model_object->sla_support_points;
 | 
			
		||||
 | 
			
		||||
    // this could be cached as well
 | 
			
		||||
    std::vector<Vec3d> ret; ret.reserve(spts.size());
 | 
			
		||||
    std::vector<sla::SupportPoint> ret;
 | 
			
		||||
    ret.reserve(spts.size());
 | 
			
		||||
 | 
			
		||||
    for(auto& sp : spts) ret.emplace_back( trafo() * Vec3d(sp.cast<double>()));
 | 
			
		||||
    for(sla::SupportPoint& sp : spts) {
 | 
			
		||||
        Vec3d transformed_pos = trafo() * Vec3d(sp.pos(0), sp.pos(1), sp.pos(2));
 | 
			
		||||
        ret.emplace_back(transformed_pos(0), transformed_pos(1), transformed_pos(2), sp.head_front_radius, sp.is_new_island);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ public:
 | 
			
		|||
    // This will return the transformed mesh which is cached
 | 
			
		||||
    const TriangleMesh&     transformed_mesh() const;
 | 
			
		||||
 | 
			
		||||
    std::vector<Vec3d>      transformed_support_points() const;
 | 
			
		||||
    std::vector<sla::SupportPoint>      transformed_support_points() const;
 | 
			
		||||
 | 
			
		||||
    // Get the needed Z elevation for the model geometry if supports should be
 | 
			
		||||
    // displayed. This Z offset should also be applied to the support
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,8 +22,6 @@
 | 
			
		|||
// Scene's GUI made using imgui library
 | 
			
		||||
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
 | 
			
		||||
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
 | 
			
		||||
// Modified Sla support gizmo
 | 
			
		||||
#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1)
 | 
			
		||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
 | 
			
		||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3381,22 +3381,14 @@ void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object)
 | 
			
		|||
        reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection)
 | 
			
		||||
#else
 | 
			
		||||
void GLCanvas3D::Gizmos::set_model_object_ptr(ModelObject* model_object)
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
{
 | 
			
		||||
    if (!m_enabled)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
 | 
			
		||||
    if (it != m_gizmos.end())
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_sla_support_data(model_object, selection);
 | 
			
		||||
#else
 | 
			
		||||
        reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_model_object_ptr(model_object);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::Gizmos::clicked_on_object(const Vec2d& mouse_position)
 | 
			
		||||
| 
						 | 
				
			
			@ -7137,11 +7129,7 @@ void GLCanvas3D::_update_gizmos_data()
 | 
			
		|||
        m_gizmos.set_rotation(Vec3d::Zero());
 | 
			
		||||
        ModelObject* model_object = m_model->objects[m_selection.get_object_idx()];
 | 
			
		||||
        m_gizmos.set_flattening_data(model_object);
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        m_gizmos.set_sla_support_data(model_object, m_selection);
 | 
			
		||||
#else
 | 
			
		||||
        m_gizmos.set_model_object_ptr(model_object);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    }
 | 
			
		||||
    else if (m_selection.is_single_volume() || m_selection.is_single_modifier())
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -7149,22 +7137,14 @@ void GLCanvas3D::_update_gizmos_data()
 | 
			
		|||
        m_gizmos.set_scale(volume->get_volume_scaling_factor());
 | 
			
		||||
        m_gizmos.set_rotation(Vec3d::Zero());
 | 
			
		||||
        m_gizmos.set_flattening_data(nullptr);
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        m_gizmos.set_sla_support_data(nullptr, m_selection);
 | 
			
		||||
#else
 | 
			
		||||
        m_gizmos.set_model_object_ptr(nullptr);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        m_gizmos.set_scale(Vec3d::Ones());
 | 
			
		||||
        m_gizmos.set_rotation(Vec3d::Zero());
 | 
			
		||||
        m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr);
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        m_gizmos.set_sla_support_data(nullptr, m_selection);
 | 
			
		||||
#else
 | 
			
		||||
        m_gizmos.set_model_object_ptr(nullptr);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -779,11 +779,7 @@ private:
 | 
			
		|||
 | 
			
		||||
        void set_flattening_data(const ModelObject* model_object);
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
 | 
			
		||||
#else
 | 
			
		||||
        void set_model_object_ptr(ModelObject* model_object);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        void clicked_on_object(const Vec2d& mouse_position);
 | 
			
		||||
        void delete_current_grabber(bool delete_all = false);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1741,28 +1741,20 @@ Vec3d GLGizmoFlatten::get_flattening_normal() const
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent)
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    : GLGizmoBase(parent), m_starting_center(Vec3d::Zero()), m_quadric(nullptr)
 | 
			
		||||
#else
 | 
			
		||||
    : GLGizmoBase(parent), m_starting_center(Vec3d::Zero())
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
{
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    m_quadric = ::gluNewQuadric();
 | 
			
		||||
    if (m_quadric != nullptr)
 | 
			
		||||
        // using GLU_FILL does not work when the instance's transformation
 | 
			
		||||
        // contains mirroring (normals are reverted)
 | 
			
		||||
        ::gluQuadricDrawStyle(m_quadric, GLU_FILL);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
GLGizmoSlaSupports::~GLGizmoSlaSupports()
 | 
			
		||||
{
 | 
			
		||||
    if (m_quadric != nullptr)
 | 
			
		||||
        ::gluDeleteQuadric(m_quadric);
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
bool GLGizmoSlaSupports::on_init()
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1782,7 +1774,6 @@ bool GLGizmoSlaSupports::on_init()
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection)
 | 
			
		||||
{
 | 
			
		||||
    m_starting_center = Vec3d::Zero();
 | 
			
		||||
| 
						 | 
				
			
			@ -1801,59 +1792,37 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G
 | 
			
		|||
            for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
 | 
			
		||||
                if (po->model_object()->id() == model_object->id()) {
 | 
			
		||||
                    const Eigen::MatrixXd& points = po->get_support_points();
 | 
			
		||||
                    for (unsigned int i=0; i<points.rows();++i)
 | 
			
		||||
                        model_object->sla_support_points.push_back(Vec3f(po->trafo().inverse().cast<float>() * Vec3f(points(i,0), points(i,1), points(i,2))));
 | 
			
		||||
                        break;
 | 
			
		||||
                    for (unsigned int i=0; i<points.rows();++i) {
 | 
			
		||||
                        Vec3f pos(po->trafo().inverse().cast<float>() * Vec3f(points(i,0), points(i,1), points(i,2)));
 | 
			
		||||
                        model_object->sla_support_points.emplace_back(pos(0), pos(1), pos(2), points(i, 3), points(i, 4));
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object)
 | 
			
		||||
{
 | 
			
		||||
    if (model_object != nullptr) {
 | 
			
		||||
        m_starting_center = Vec3d::Zero();
 | 
			
		||||
        m_model_object = model_object;
 | 
			
		||||
 | 
			
		||||
        int selected_instance = m_parent.get_selection().get_instance_idx();
 | 
			
		||||
        assert(selected_instance < (int)model_object->instances.size());
 | 
			
		||||
 | 
			
		||||
        m_instance_matrix = model_object->instances[selected_instance]->get_matrix();
 | 
			
		||||
        if (is_mesh_update_necessary())
 | 
			
		||||
            update_mesh();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const
 | 
			
		||||
{
 | 
			
		||||
    ::glEnable(GL_BLEND);
 | 
			
		||||
    ::glEnable(GL_DEPTH_TEST);
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    // the dragged_offset is a vector measuring where was the object moved
 | 
			
		||||
    // with the gizmo being on. This is reset in set_model_object_ptr and
 | 
			
		||||
    // does not work correctly when there are multiple copies.
 | 
			
		||||
    
 | 
			
		||||
    if (m_starting_center == Vec3d::Zero())
 | 
			
		||||
        m_starting_center = selection.get_bounding_box().center();
 | 
			
		||||
    Vec3d dragged_offset = selection.get_bounding_box().center() - m_starting_center;
 | 
			
		||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    for (auto& g : m_grabbers) {
 | 
			
		||||
        g.color[0] = 1.f;
 | 
			
		||||
        g.color[1] = 0.f;
 | 
			
		||||
        g.color[2] = 0.f;
 | 
			
		||||
    for (unsigned int i=0; i<m_grabbers.size(); ++i) {
 | 
			
		||||
        bool supports_new_island = m_lock_unique_islands && m_model_object && m_model_object->sla_support_points[i].is_new_island;
 | 
			
		||||
        Grabber& g = m_grabbers[i];
 | 
			
		||||
        if (m_editing_mode) {
 | 
			
		||||
            g.color[0] = supports_new_island ? 0.f : 1.f;
 | 
			
		||||
            g.color[1] = 0.f;
 | 
			
		||||
            g.color[2] = supports_new_island ? 1.f : 0.f;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            for (unsigned char i=0; i<3; ++i) g.color[i] = 0.5f;
 | 
			
		||||
        }
 | 
			
		||||
            
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    render_grabbers(selection, false);
 | 
			
		||||
#else
 | 
			
		||||
    //::glTranslatef((GLfloat)dragged_offset(0), (GLfloat)dragged_offset(1), (GLfloat)dragged_offset(2));
 | 
			
		||||
    render_grabbers(false);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_IMGUI
 | 
			
		||||
    render_tooltip_texture();
 | 
			
		||||
| 
						 | 
				
			
			@ -1870,14 +1839,9 @@ void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& sele
 | 
			
		|||
        m_grabbers[i].color[1] = 1.0f;
 | 
			
		||||
        m_grabbers[i].color[2] = picking_color_component(i);
 | 
			
		||||
    }
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    render_grabbers(selection, true);
 | 
			
		||||
#else
 | 
			
		||||
    render_grabbers(true);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection, bool picking) const
 | 
			
		||||
{
 | 
			
		||||
    if (m_quadric == nullptr)
 | 
			
		||||
| 
						 | 
				
			
			@ -1924,8 +1888,7 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection,
 | 
			
		|||
        ::glPushMatrix();
 | 
			
		||||
        ::glLoadIdentity();
 | 
			
		||||
        ::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift);
 | 
			
		||||
        const float diameter = 0.8f;
 | 
			
		||||
        ::gluSphere(m_quadric, diameter/2.f, 64, 36);
 | 
			
		||||
        ::gluSphere(m_quadric, m_model_object->sla_support_points[i].head_front_radius, 64, 36);
 | 
			
		||||
        ::glPopMatrix();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1934,65 +1897,10 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection,
 | 
			
		|||
 | 
			
		||||
    ::glPopMatrix();
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void GLGizmoSlaSupports::render_grabbers(bool picking) const
 | 
			
		||||
{
 | 
			
		||||
	if (m_parent.get_selection().is_empty())
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
    float z_shift = m_parent.get_selection().get_volume(0)->get_sla_shift_z();
 | 
			
		||||
    ::glTranslatef((GLfloat)0, (GLfloat)0, (GLfloat)z_shift);
 | 
			
		||||
 | 
			
		||||
    int selected_instance = m_parent.get_selection().get_instance_idx();
 | 
			
		||||
    assert(selected_instance < (int)m_model_object->instances.size());
 | 
			
		||||
 | 
			
		||||
    float render_color_inactive[3] = { 0.5f, 0.5f, 0.5f };
 | 
			
		||||
 | 
			
		||||
    for (const ModelInstance* inst : m_model_object->instances) {
 | 
			
		||||
		bool active = inst == m_model_object->instances[selected_instance];
 | 
			
		||||
        if (picking && ! active)
 | 
			
		||||
            continue;
 | 
			
		||||
        for (int i = 0; i < (int)m_grabbers.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            if (!m_grabbers[i].enabled)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            float render_color[3];
 | 
			
		||||
            if (! picking && active && m_hover_id == i) {
 | 
			
		||||
                render_color[0] = 1.0f - m_grabbers[i].color[0];
 | 
			
		||||
                render_color[1] = 1.0f - m_grabbers[i].color[1];
 | 
			
		||||
                render_color[2] = 1.0f - m_grabbers[i].color[2];
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                ::memcpy((void*)render_color, active ? (const void*)m_grabbers[i].color : (const void*)render_color_inactive, 3 * sizeof(float));
 | 
			
		||||
            if (!picking)
 | 
			
		||||
                ::glEnable(GL_LIGHTING);
 | 
			
		||||
            ::glColor3f((GLfloat)render_color[0], (GLfloat)render_color[1], (GLfloat)render_color[2]);
 | 
			
		||||
            ::glPushMatrix();
 | 
			
		||||
            Vec3d center = inst->get_matrix() * m_grabbers[i].center;
 | 
			
		||||
            ::glTranslatef((GLfloat)center(0), (GLfloat)center(1), (GLfloat)center(2));
 | 
			
		||||
            GLUquadricObj *quadric;
 | 
			
		||||
            quadric = ::gluNewQuadric();
 | 
			
		||||
            ::gluQuadricDrawStyle(quadric, GLU_FILL );
 | 
			
		||||
            ::gluSphere( quadric , 0.4, 64 , 32 );
 | 
			
		||||
            ::gluDeleteQuadric(quadric);
 | 
			
		||||
            ::glPopMatrix();
 | 
			
		||||
            if (!picking)
 | 
			
		||||
                ::glDisable(GL_LIGHTING);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ::glTranslatef((GLfloat)0, (GLfloat)0, (GLfloat)-z_shift);
 | 
			
		||||
}
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
 | 
			
		||||
{
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    return (m_state == On) && (m_model_object != nullptr) && (m_model_object != m_old_model_object) && !m_model_object->instances.empty();
 | 
			
		||||
#else
 | 
			
		||||
    return m_state == On && m_model_object && !m_model_object->instances.empty() && !m_instance_matrix.isApprox(m_source_data.matrix);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    //if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
 | 
			
		||||
    //    return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -2027,27 +1935,19 @@ void GLGizmoSlaSupports::update_mesh()
 | 
			
		|||
    m_AABB = igl::AABB<Eigen::MatrixXf,3>();
 | 
			
		||||
    m_AABB.init(m_V, m_F);
 | 
			
		||||
 | 
			
		||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    m_source_data.matrix = m_instance_matrix;
 | 
			
		||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    // we'll now reload Grabbers (selection might have changed):
 | 
			
		||||
    m_grabbers.clear();
 | 
			
		||||
 | 
			
		||||
    for (const Vec3f& point : m_model_object->sla_support_points) {
 | 
			
		||||
    for (const sla::SupportPoint& point : m_model_object->sla_support_points) {
 | 
			
		||||
        m_grabbers.push_back(Grabber());
 | 
			
		||||
        m_grabbers.back().center = point.cast<double>();
 | 
			
		||||
        m_grabbers.back().center = point.pos.cast<double>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
 | 
			
		||||
{
 | 
			
		||||
    // if the gizmo doesn't have the V, F structures for igl, calculate them first:
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    if (m_V.size() == 0)
 | 
			
		||||
#else
 | 
			
		||||
    if (m_V.size() == 0 || is_mesh_update_necessary())
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        update_mesh();
 | 
			
		||||
 | 
			
		||||
    Eigen::Matrix<GLint, 4, 1, Eigen::DontAlign> viewport;
 | 
			
		||||
| 
						 | 
				
			
			@ -2064,20 +1964,15 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
 | 
			
		|||
 | 
			
		||||
    igl::Hit hit;
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    const GLCanvas3D::Selection& selection = m_parent.get_selection();
 | 
			
		||||
    const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
 | 
			
		||||
    double z_offset = volume->get_sla_shift_z();
 | 
			
		||||
#else
 | 
			
		||||
    double z_offset = m_parent.get_selection().get_volume(0)->get_sla_shift_z();
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    point1(2) -= z_offset;
 | 
			
		||||
	point2(2) -= z_offset;
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    Transform3d inv = volume->get_instance_transformation().get_matrix().inverse();
 | 
			
		||||
#else
 | 
			
		||||
    Transform3d inv = m_instance_matrix.inverse();
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    point1 = inv * point1;
 | 
			
		||||
    point2 = inv * point2;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2091,7 +1986,9 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
 | 
			
		|||
 | 
			
		||||
void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position)
 | 
			
		||||
{
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    if (!m_editing_mode)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    int instance_id = m_parent.get_selection().get_instance_idx();
 | 
			
		||||
    if (m_old_instance_id != instance_id)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -2102,7 +1999,6 @@ void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position)
 | 
			
		|||
    }
 | 
			
		||||
    if (instance_id == -1)
 | 
			
		||||
        return;
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    Vec3f new_pos;
 | 
			
		||||
    try {
 | 
			
		||||
| 
						 | 
				
			
			@ -2112,7 +2008,8 @@ void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position)
 | 
			
		|||
 | 
			
		||||
    m_grabbers.push_back(Grabber());
 | 
			
		||||
    m_grabbers.back().center = new_pos.cast<double>();
 | 
			
		||||
    m_model_object->sla_support_points.push_back(new_pos);
 | 
			
		||||
    
 | 
			
		||||
    m_model_object->sla_support_points.emplace_back(new_pos, m_new_point_head_diameter, false);
 | 
			
		||||
 | 
			
		||||
    // This should trigger the support generation
 | 
			
		||||
    // wxGetApp().plater()->reslice();
 | 
			
		||||
| 
						 | 
				
			
			@ -2122,6 +2019,9 @@ void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position)
 | 
			
		|||
 | 
			
		||||
void GLGizmoSlaSupports::delete_current_grabber(bool delete_all)
 | 
			
		||||
{
 | 
			
		||||
    if (!m_editing_mode && !delete_all)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (delete_all) {
 | 
			
		||||
        m_grabbers.clear();
 | 
			
		||||
        m_model_object->sla_support_points.clear();
 | 
			
		||||
| 
						 | 
				
			
			@ -2131,26 +2031,29 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all)
 | 
			
		|||
    }
 | 
			
		||||
    else
 | 
			
		||||
        if (m_hover_id != -1) {
 | 
			
		||||
            m_grabbers.erase(m_grabbers.begin() + m_hover_id);
 | 
			
		||||
            m_model_object->sla_support_points.erase(m_model_object->sla_support_points.begin() + m_hover_id);
 | 
			
		||||
            m_hover_id = -1;
 | 
			
		||||
            if (!m_model_object->sla_support_points[m_hover_id].is_new_island || !m_lock_unique_islands) {
 | 
			
		||||
                m_grabbers.erase(m_grabbers.begin() + m_hover_id);
 | 
			
		||||
                m_model_object->sla_support_points.erase(m_model_object->sla_support_points.begin() + m_hover_id);
 | 
			
		||||
                m_hover_id = -1;
 | 
			
		||||
 | 
			
		||||
            // This should trigger the support generation
 | 
			
		||||
            // wxGetApp().plater()->reslice();
 | 
			
		||||
                // This should trigger the support generation
 | 
			
		||||
                // wxGetApp().plater()->reslice();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
 | 
			
		||||
{
 | 
			
		||||
    if (m_hover_id != -1 && data.mouse_pos) {
 | 
			
		||||
    if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_model_object->sla_support_points[m_hover_id].is_new_island || !m_lock_unique_islands)) {
 | 
			
		||||
        Vec3f new_pos;
 | 
			
		||||
        try {
 | 
			
		||||
            new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
 | 
			
		||||
        }
 | 
			
		||||
        catch (...) { return; }
 | 
			
		||||
        m_grabbers[m_hover_id].center = new_pos.cast<double>();
 | 
			
		||||
        m_model_object->sla_support_points[m_hover_id] = new_pos;
 | 
			
		||||
        m_model_object->sla_support_points[m_hover_id].pos = new_pos;
 | 
			
		||||
        m_model_object->sla_support_points[m_hover_id].is_new_island = false;
 | 
			
		||||
        // Do not update immediately, wait until the mouse is released.
 | 
			
		||||
        // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2196,14 +2099,46 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas
 | 
			
		|||
RENDER_AGAIN:
 | 
			
		||||
    m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
 | 
			
		||||
    m_imgui->set_next_window_bg_alpha(0.5f);
 | 
			
		||||
    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
 | 
			
		||||
    m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove |/* ImGuiWindowFlags_NoResize | */ImGuiWindowFlags_NoCollapse);
 | 
			
		||||
 | 
			
		||||
    ImGui::PushItemWidth(100.0f);
 | 
			
		||||
    m_imgui->text(_(L("Left mouse click - add point")));
 | 
			
		||||
    m_imgui->text(_(L("Right mouse click - remove point")));
 | 
			
		||||
    m_imgui->text(" ");
 | 
			
		||||
    
 | 
			
		||||
    bool force_refresh = m_editing_mode;
 | 
			
		||||
    if (m_imgui->radio_button(_(L("Automatic")), !m_editing_mode))
 | 
			
		||||
        m_editing_mode = false;
 | 
			
		||||
    ImGui::SameLine();
 | 
			
		||||
    if (m_imgui->radio_button(_(L("Manual")), m_editing_mode))
 | 
			
		||||
        m_editing_mode = true;
 | 
			
		||||
    force_refresh = force_refresh != m_editing_mode;
 | 
			
		||||
    m_imgui->text("");
 | 
			
		||||
    
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    bool generate = m_imgui->button(_(L("Generate points automatically")));
 | 
			
		||||
    if (m_editing_mode) {
 | 
			
		||||
        m_imgui->text(_(L("Left mouse click - add point")));
 | 
			
		||||
        m_imgui->text(_(L("Right mouse click - remove point")));
 | 
			
		||||
        m_imgui->text(" ");
 | 
			
		||||
        
 | 
			
		||||
      
 | 
			
		||||
        std::vector<wxString> options = {"0.2", "0.4", "0.6", "0.8", "1.0"};
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << std::setprecision(1) << m_new_point_head_diameter;
 | 
			
		||||
        wxString str = ss.str();
 | 
			
		||||
        m_imgui->combo(_(L("Head diameter")), options, str);
 | 
			
		||||
        force_refresh |= std::abs(atof(str) - m_new_point_head_diameter) > 0.001;
 | 
			
		||||
        m_new_point_head_diameter = atof(str);
 | 
			
		||||
 | 
			
		||||
        bool changed = m_lock_unique_islands;
 | 
			
		||||
        m_imgui->checkbox(_(L("Lock supports under new islands")), m_lock_unique_islands);
 | 
			
		||||
        force_refresh |= changed != m_lock_unique_islands;                
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        bool generate =m_imgui->button(_(L("Auto-generate points")));
 | 
			
		||||
        force_refresh |= generate;
 | 
			
		||||
        if (generate)
 | 
			
		||||
            wxGetApp().plater()->reslice();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool remove_all_clicked = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_model_object->sla_support_points.size())+")"));
 | 
			
		||||
 | 
			
		||||
    m_imgui->end();
 | 
			
		||||
| 
						 | 
				
			
			@ -2216,7 +2151,7 @@ RENDER_AGAIN:
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (remove_all_clicked || generate) {
 | 
			
		||||
    if (remove_all_clicked || force_refresh) {
 | 
			
		||||
        m_parent.reload_scene(true);
 | 
			
		||||
        m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -441,27 +441,16 @@ class GLGizmoSlaSupports : public GLGizmoBase
 | 
			
		|||
{
 | 
			
		||||
private:
 | 
			
		||||
    ModelObject* m_model_object = nullptr;
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    ModelObject* m_old_model_object = nullptr;
 | 
			
		||||
    int m_old_instance_id = -1;
 | 
			
		||||
#else
 | 
			
		||||
    Transform3d m_instance_matrix;
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    Vec3f unproject_on_mesh(const Vec2d& mouse_pos);
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    GLUquadricObj* m_quadric;
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
 | 
			
		||||
    Eigen::MatrixXf m_V; // vertices
 | 
			
		||||
    Eigen::MatrixXi m_F; // facets indices
 | 
			
		||||
    igl::AABB<Eigen::MatrixXf,3> m_AABB;
 | 
			
		||||
 | 
			
		||||
    struct SourceDataSummary {
 | 
			
		||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        BoundingBoxf3 bounding_box;
 | 
			
		||||
        Transform3d matrix;
 | 
			
		||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
        Vec3d mesh_first_point;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -472,12 +461,8 @@ private:
 | 
			
		|||
 | 
			
		||||
public:
 | 
			
		||||
    explicit GLGizmoSlaSupports(GLCanvas3D& parent);
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    virtual ~GLGizmoSlaSupports();
 | 
			
		||||
    void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
 | 
			
		||||
#else
 | 
			
		||||
    void set_model_object_ptr(ModelObject* model_object);
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    void clicked_on_object(const Vec2d& mouse_position);
 | 
			
		||||
    void delete_current_grabber(bool delete_all);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -487,11 +472,7 @@ private:
 | 
			
		|||
    virtual void on_render(const GLCanvas3D::Selection& selection) const;
 | 
			
		||||
    virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
 | 
			
		||||
 | 
			
		||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    void render_grabbers(const GLCanvas3D::Selection& selection, bool picking = false) const;
 | 
			
		||||
#else
 | 
			
		||||
    void render_grabbers(bool picking = false) const;
 | 
			
		||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
 | 
			
		||||
    bool is_mesh_update_necessary() const;
 | 
			
		||||
    void update_mesh();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -501,6 +482,10 @@ private:
 | 
			
		|||
    mutable GLTexture m_reset_texture;
 | 
			
		||||
#endif // not ENABLE_IMGUI
 | 
			
		||||
 | 
			
		||||
    bool m_lock_unique_islands = false;
 | 
			
		||||
    bool m_editing_mode = false;
 | 
			
		||||
    float m_new_point_head_diameter = 0.4f;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void on_set_state() override {
 | 
			
		||||
        if (m_state == On && is_mesh_update_necessary()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,6 +125,12 @@ bool ImGuiWrapper::button(const wxString &label)
 | 
			
		|||
    return ImGui::Button(label_utf8.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
 | 
			
		||||
{
 | 
			
		||||
    auto label_utf8 = into_u8(label);
 | 
			
		||||
    return ImGui::RadioButton(label_utf8.c_str(), active);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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());
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +167,26 @@ void ImGuiWrapper::text(const wxString &label)
 | 
			
		|||
    ImGui::Text(label_utf8.c_str(), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ImGuiWrapper::combo(const wxString& label, const std::vector<wxString>& options, wxString& selection)
 | 
			
		||||
{
 | 
			
		||||
    const char* selection_u8 = into_u8(selection).c_str();
 | 
			
		||||
 | 
			
		||||
    // this is to force the label to the left of the widget:
 | 
			
		||||
    text(label);
 | 
			
		||||
    ImGui::SameLine();
 | 
			
		||||
    
 | 
			
		||||
    if (ImGui::BeginCombo("", selection_u8)) {
 | 
			
		||||
        for (const wxString& option : options) {
 | 
			
		||||
            const char* option_u8 = into_u8(option).c_str();
 | 
			
		||||
            bool is_selected = (selection_u8 == nullptr) ? false : strcmp(option_u8, selection_u8) == 0;
 | 
			
		||||
            if (ImGui::Selectable(option_u8, is_selected))
 | 
			
		||||
                selection = option_u8;
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::EndCombo();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ImGuiWrapper::disabled_begin(bool disabled)
 | 
			
		||||
{
 | 
			
		||||
    wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,10 +47,12 @@ public:
 | 
			
		|||
    void end();
 | 
			
		||||
 | 
			
		||||
    bool button(const wxString &label);
 | 
			
		||||
    bool radio_button(const wxString &label, bool active);
 | 
			
		||||
    bool input_double(const std::string &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");
 | 
			
		||||
    bool checkbox(const wxString &label, bool &value);
 | 
			
		||||
    void text(const wxString &label);
 | 
			
		||||
    void combo(const wxString& label, const std::vector<wxString>& options, wxString& current_selection);
 | 
			
		||||
 | 
			
		||||
    void disabled_begin(bool disabled);
 | 
			
		||||
    void disabled_end();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue