From 21026ec9a80928a4ee71787227830d4864510e76 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 30 Jan 2019 08:26:23 +0100 Subject: [PATCH] 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 --- src/libslic3r/Format/3mf.cpp | 16 +- src/libslic3r/Format/AMF.cpp | 10 +- src/libslic3r/Model.hpp | 4 +- src/libslic3r/SLA/SLAAutoSupports.cpp | 503 ++++++++++++-------------- src/libslic3r/SLA/SLAAutoSupports.hpp | 43 ++- src/libslic3r/SLA/SLASupportTree.cpp | 14 +- src/libslic3r/SLA/SLASupportTree.hpp | 22 +- src/libslic3r/SLAPrint.cpp | 14 +- src/libslic3r/SLAPrint.hpp | 2 +- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 20 - src/slic3r/GUI/GLCanvas3D.hpp | 4 - src/slic3r/GUI/GLGizmo.cpp | 223 ++++-------- src/slic3r/GUI/GLGizmo.hpp | 23 +- src/slic3r/GUI/ImGuiWrapper.cpp | 26 ++ src/slic3r/GUI/ImGuiWrapper.hpp | 2 + 16 files changed, 433 insertions(+), 495 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 8b0c28cd6..d6f20ed98 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -323,7 +323,7 @@ namespace Slic3r { typedef std::map IdToMetadataMap; typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; - typedef std::map> IdToSlaSupportPointsMap; + typedef std::map> IdToSlaSupportPointsMap; // Version of the 3mf file unsigned int m_version; @@ -811,10 +811,14 @@ namespace Slic3r { std::vector object_data_points; boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off); - std::vector sla_support_points; + std::vector sla_support_points; - for (unsigned int i=0; i()); + for (unsigned int i=0; i& sla_support_points = object->sla_support_points; + const std::vector& 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"; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 9791b1502..81ca67129 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -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 point(Eigen::Matrix::Zero()); char *p = const_cast(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& sla_support_points = object->sla_support_points; + const std::vector& sla_support_points = object->sla_support_points; if (!sla_support_points.empty()) { // Store the SLA supports as a single semicolon separated list. stream << " "; 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 \n"; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index ba109246a..0477e34cb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -12,6 +12,7 @@ #include #include #include "Geometry.hpp" +#include 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 sla_support_points; + // The format is (x, y, z, point_size, supports_island) + std::vector 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 diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index ee87c6b66..256d9f3fc 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -13,34 +13,7 @@ namespace Slic3r { -SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, - const Config& config, std::function 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> islands = find_islands(slices, heights); - - // Uniformly cover each of the islands with support points. - for (const auto& island : islands) { - std::vector 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 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() * point; - - - // In order to calculate distance to already placed points, we must keep know which facet the point lies on. - std::vector facets_normals; - - // Only points belonging to islands were added so far - they all lie on horizontal surfaces: - for (unsigned int i=0; i aabb; - aabb.init(V, F); - for (unsigned int i=0; i 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()); - if (angle > threshold_angle) - continue; - - const float limit = distance_limit(angle); - bool add_it = true; - for (unsigned int i=0; i()); - 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() * 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& slices, const std::vector& heights, + const Config& config, std::function 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> islands = */ + process(slices, heights); + + // Uniformly cover each of the islands with support points. + /*for (const auto& island : islands) { + std::vector 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& 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(), Vec3f(0., 0., 1.), m_V, m_F, hit_up); + bool down = igl::ray_mesh_intersect(p.cast(), 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(); + } +} + + + + +void SLAAutoSupports::process(const std::vector& slices, const std::vector& heights) +{ + std::vector> islands; + + for (unsigned int i = 0; i2 ? 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 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 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& structures = m_structures_new; + for (unsigned int i=0 ; i{*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> SLAAutoSupports::find_islands(const std::vector& slices, const std::vector& heights) const -{ - std::vector> islands; - - struct PointAccessor { - const Point* operator()(const Point &pt) const { return &pt; } - }; - typedef ClosestPointInRadiusLookup ClosestPointLookupType; - - for (unsigned int i = 0; i SLAAutoSupports::uniformly_cover(const std::pair& 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{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 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& 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(), 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(); - } -} - +#endif } // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 311d7b0c7..f126e3600 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -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& slices, - const std::vector& heights, const Config& config, std::function throw_on_cancel); - const std::vector& output() { return m_output; } + SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, + const std::vector& heights, const Config& config, std::function throw_on_cancel); + const std::vector& output() { return m_output; } private: - std::vector m_output; - std::vector 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> find_islands(const std::vector& slices, const std::vector& heights) const; - void sprinkle_mesh(const TriangleMesh& mesh); - std::vector uniformly_cover(const std::pair& island); - void project_upward_onto_mesh(std::vector& points) const; + std::vector 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::system_clock::now().time_since_epoch())) { + polygon = &poly; + } + const ExPolygon* polygon = nullptr; + std::vector structures_below; + float height = 0; + float supports_force = 0.f; + std::chrono::milliseconds unique_id; + }; + std::vector m_structures_old; + std::vector m_structures_new; + float m_supports_force_total = 0.f; + + void process(const std::vector& slices, const std::vector& 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& points) const; + + std::function m_throw_on_cancel; const Eigen::MatrixXd& m_V; const Eigen::MatrixXi& m_F; diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 763166d72..db165da7f 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -607,10 +607,18 @@ EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { return to_eigenmesh(modelobj.raw_mesh()); } -PointSet to_point_set(const std::vector &v) +PointSet to_point_set(const std::vector &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; } diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 5a86d4623..2a0be806a 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -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 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&); +PointSet to_point_set(const std::vector&); /* ************************************************************************** */ diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index bd0a57352..3d77fc2b7 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -541,7 +541,7 @@ void SLAPrint::process() [this]() { throw_if_canceled(); }); // Now let's extract the result. - const std::vector& points = auto_supports.output(); + const std::vector& 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 SLAPrintObject::transformed_support_points() const +std::vector SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); - auto& spts = m_model_object->sla_support_points; + std::vector& spts = m_model_object->sla_support_points; // this could be cached as well - std::vector ret; ret.reserve(spts.size()); + std::vector ret; + ret.reserve(spts.size()); - for(auto& sp : spts) ret.emplace_back( trafo() * Vec3d(sp.cast())); + 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; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 21503c6f6..fc4e9a4d4 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -70,7 +70,7 @@ public: // This will return the transformed mesh which is cached const TriangleMesh& transformed_mesh() const; - std::vector transformed_support_points() const; + std::vector 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 diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index b6b69ae27..c61d997ee 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -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) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 33f62f205..30ace0e81 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3381,22 +3381,14 @@ void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) reinterpret_cast(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(it->second)->set_sla_support_data(model_object, selection); -#else - reinterpret_cast(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 } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index cbc5013ea..9508b496e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -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); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 1660976a1..dcd99bfa6 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -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; isla_support_points.push_back(Vec3f(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)))); - break; + for (unsigned int i=0; itrafo().inverse().cast() * 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; isla_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(); 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(); + m_grabbers.back().center = point.pos.cast(); } } 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 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(); - 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(); - 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 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)); } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 7a55a2392..7e2a3bb0b 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -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 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()) { diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e36a68eda..4f5cbf749 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -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(&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& 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"); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 47a1fb937..a53718162 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -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& options, wxString& current_selection); void disabled_begin(bool disabled); void disabled_end();