From 21026ec9a80928a4ee71787227830d4864510e76 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 30 Jan 2019 08:26:23 +0100 Subject: [PATCH 01/28] 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(); From f568f93f08bbb44f1dbb36491158179d9ab8d001 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Feb 2019 16:12:00 +0100 Subject: [PATCH 02/28] More SLA support points improvements - unselected objects are hidden when SLA gizmo is active - support volumes are hidden when editing mode is active - 3mf support points format versioning --- src/libslic3r/Format/3mf.cpp | 24 ++++- src/libslic3r/Format/3mf.hpp | 17 +++ src/libslic3r/Model.hpp | 2 +- src/libslic3r/SLA/SLAAutoSupports.cpp | 32 ++---- src/libslic3r/SLA/SLAAutoSupports.hpp | 2 +- src/libslic3r/SLA/SLACommon.hpp | 48 +++++++++ src/libslic3r/SLA/SLASupportTree.hpp | 31 +----- src/slic3r/GUI/GLCanvas3D.cpp | 29 ++++- src/slic3r/GUI/GLCanvas3D.hpp | 4 + src/slic3r/GUI/GLGizmo.cpp | 149 +++++++++++++++++++------- src/slic3r/GUI/GLGizmo.hpp | 7 +- 11 files changed, 248 insertions(+), 97 deletions(-) create mode 100644 src/libslic3r/SLA/SLACommon.hpp diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index d6f20ed98..939ec7450 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -776,10 +776,19 @@ namespace Slic3r { std::vector objects; boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off); + // Info on format versioning - see 3mf.hpp + int version = 0; + if (!objects.empty() && objects[0].find("support_points_format_version=") != std::string::npos) { + objects[0].erase(objects[0].begin(), objects[0].begin() + 30); // removes the string + version = std::stoi(objects[0]); + objects.erase(objects.begin()); // pop the header + } + for (const std::string& object : objects) { std::vector object_data; boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off); + if (object_data.size() != 2) { add_error("Error while reading object data"); @@ -813,12 +822,22 @@ namespace Slic3r { std::vector sla_support_points; - for (unsigned int i=0; i #include #include "Geometry.hpp" -#include +#include namespace Slic3r { diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 256d9f3fc..c08464199 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -52,20 +52,7 @@ SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3 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); } @@ -104,10 +91,14 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: const ExPolygons& expolys_top = slices[i]; const float height = (i>2 ? heights[i-3] : heights[0]-(heights[1]-heights[0])); + const float layer_height = (i!=0 ? heights[i]-heights[i-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 + const float offset = scale_(layer_height / std::tan(safe_angle)); + + // FIXME: calculate actual pixel area from printer config: + //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // + const float pixel_area = pow(0.047f, 2.f); // Check all ExPolygons on this slice and check whether they are new or belonging to something below. for (const ExPolygon& polygon : expolys_top) { @@ -119,12 +110,11 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: 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())); + float mult = std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * (float)(centroids_dist * centroids_dist) / (float)bottom->area())); + s.supports_force *= mult; + s.supports_force *= std::min(1.f, 20.f * ((float)bottom->area() / (float)polygon.area())); } } } @@ -176,7 +166,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: } e = diff_ex(ExPolygons{*s.polygon}, e); - s.supports_force /= std::max(1., (e_area / (s.polygon->area()*SCALING_FACTOR*SCALING_FACTOR))); + s.supports_force /= std::max(1., (layer_height / 0.3f) * (e_area / (s.polygon->area()*SCALING_FACTOR*SCALING_FACTOR))); diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index f126e3600..336f7acfa 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include // #define SLA_AUTOSUPPORTS_DEBUG diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp new file mode 100644 index 000000000..c3955bab0 --- /dev/null +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -0,0 +1,48 @@ +#ifndef SLACOMMON_HPP +#define SLACOMMON_HPP + +#include + + +namespace Slic3r { + +// Typedef from Point.hpp +typedef Eigen::Matrix Vec3f; + +namespace sla { + +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; } +}; + + +/// An index-triangle structure for libIGL functions. Also serves as an +/// alternative (raw) input format for the SLASupportTree +struct EigenMesh3D { + Eigen::MatrixXd V; + Eigen::MatrixXi F; + double ground_level = 0; +}; + + +} // namespace sla +} // namespace Slic3r + + +#endif // SLASUPPORTTREE_HPP \ No newline at end of file diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 2a0be806a..85202e498 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -7,6 +7,9 @@ #include #include +#include "SLACommon.hpp" + + namespace Slic3r { // Needed types from Point.hpp @@ -34,26 +37,6 @@ 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; @@ -122,14 +105,6 @@ struct Controller { std::function cancelfn = [](){}; }; -/// An index-triangle structure for libIGL functions. Also serves as an -/// alternative (raw) input format for the SLASupportTree -struct EigenMesh3D { - Eigen::MatrixXd V; - Eigen::MatrixXi F; - double ground_level = 0; -}; - using PointSet = Eigen::MatrixXd; EigenMesh3D to_eigenmesh(const TriangleMesh& m); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 30ace0e81..d25037dc1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4031,6 +4031,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_moving(false) , m_color_by("volume") , m_reload_delayed(false) + , m_render_sla_auxiliaries(true) #if !ENABLE_IMGUI , m_external_gizmo_widgets_parent(nullptr) #endif // not ENABLE_IMGUI @@ -4186,6 +4187,27 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } +void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible) +{ + for (GLVolume* vol : m_volumes.volumes) { + if (vol->composite_id.volume_id < 0) + vol->is_active = visible; + } + + m_render_sla_auxiliaries = visible; +} + +void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo) +{ + for (GLVolume* vol : m_volumes.volumes) { + if (mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + vol->is_active = visible; + } + if (visible && !mo) + toggle_sla_auxiliaries_visibility(true); +} + + void GLCanvas3D::set_config(const DynamicPrintConfig* config) { m_config = config; @@ -4732,7 +4754,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (it != model_volume_state.end() && it->geometry_id == key.geometry_id) mvs = &(*it); } - if (mvs == nullptr || force_full_scene_refresh) { + if (mvs == nullptr || force_full_scene_refresh || (volume->composite_id.volume_id < 0 && !m_render_sla_auxiliaries)) { // This GLVolume will be released. if (volume->is_wipe_tower) { // There is only one wipe tower. @@ -4841,7 +4863,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volumes_count = m_volumes.volumes.size(); for (size_t istep = 0; istep < sla_steps.size(); ++istep) - if (!instances[istep].empty()) + if (!instances[istep].empty() && m_render_sla_auxiliaries) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_use_VBOs && m_initialized); } @@ -6622,6 +6644,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const unsigned int volume_id = 0; for (GLVolume* vol : m_volumes.volumes) { + if (vol->composite_id.volume_id < 0 && !m_render_sla_auxiliaries) + continue; + if (fake_colors) { // Object picking mode. Render the object with a color encoding the object index. diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9508b496e..0813170a9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -910,6 +910,7 @@ private: bool m_multisample_allowed; bool m_regenerate_volumes; bool m_moving; + bool m_render_sla_auxiliaries; std::string m_color_by; @@ -940,6 +941,9 @@ public: void reset_volumes(); int check_volumes_outside_state() const; + void toggle_sla_auxiliaries_visibility(bool visible); + void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr); + void set_config(const DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); void set_model(Model* model); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index dcd99bfa6..6522287b5 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -7,7 +7,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Geometry.hpp" #include "libslic3r/Utils.hpp" -#include "libslic3r/SLA/SLASupportTree.hpp" +#include "libslic3r/SLA/SLACommon.hpp" #include "libslic3r/SLAPrint.hpp" #include @@ -1792,14 +1792,18 @@ 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; 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)); - } + for (unsigned int i=0; isla_support_points.emplace_back(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), + points(i, 3), points(i, 4)); break; } } } + m_editing_mode_cache = m_model_object->sla_support_points; // make a copy of ModelObject's support points + if (m_state == On) { + m_parent.toggle_model_objects_visibility(false); + m_parent.toggle_model_objects_visibility(true, m_model_object); + } } } @@ -1809,7 +1813,7 @@ void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const ::glEnable(GL_DEPTH_TEST); for (unsigned int i=0; isla_support_points[i].is_new_island; + bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].is_new_island; Grabber& g = m_grabbers[i]; if (m_editing_mode) { g.color[0] = supports_new_island ? 0.f : 1.f; @@ -1888,7 +1892,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); - ::gluSphere(m_quadric, m_model_object->sla_support_points[i].head_front_radius, 64, 36); + ::gluSphere(m_quadric, m_editing_mode_cache[i].head_front_radius, 64, 36); ::glPopMatrix(); } @@ -1938,7 +1942,7 @@ void GLGizmoSlaSupports::update_mesh() // we'll now reload Grabbers (selection might have changed): m_grabbers.clear(); - for (const sla::SupportPoint& point : m_model_object->sla_support_points) { + for (const sla::SupportPoint& point : m_editing_mode_cache) { m_grabbers.push_back(Grabber()); m_grabbers.back().center = point.pos.cast(); } @@ -2009,7 +2013,7 @@ 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.emplace_back(new_pos, m_new_point_head_diameter, false); + m_editing_mode_cache.emplace_back(new_pos, m_new_point_head_diameter, false); // This should trigger the support generation // wxGetApp().plater()->reslice(); @@ -2024,16 +2028,16 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) if (delete_all) { m_grabbers.clear(); - m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); // This should trigger the support generation // wxGetApp().plater()->reslice(); } else if (m_hover_id != -1) { - if (!m_model_object->sla_support_points[m_hover_id].is_new_island || !m_lock_unique_islands) { + if (!m_editing_mode_cache[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_editing_mode_cache.erase(m_editing_mode_cache.begin() + m_hover_id); m_hover_id = -1; // This should trigger the support generation @@ -2045,15 +2049,15 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) { - 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)) { + if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[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].pos = new_pos; - m_model_object->sla_support_points[m_hover_id].is_new_island = false; + m_editing_mode_cache[m_hover_id].pos = new_pos; + m_editing_mode_cache[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)); } @@ -2102,24 +2106,32 @@ RENDER_AGAIN: m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove |/* ImGuiWindowFlags_NoResize | */ImGuiWindowFlags_NoCollapse); ImGui::PushItemWidth(100.0f); - - bool force_refresh = m_editing_mode; + + /*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(""); - - + + if (force_refresh) { // mode has just changed! + if (m_editing_mode) + m_editing_mode_cache = m_model_object->sla_support_points; + else + m_model_object->sla_support_points = m_editing_mode_cache; + } + */ + + bool force_refresh = false; + bool remove_all_points = false; + bool old_editing_state = m_editing_mode; 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(" "); - - + m_imgui->text(" "); // vertical gap + 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; @@ -2130,30 +2142,77 @@ RENDER_AGAIN: 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; + force_refresh |= changed != m_lock_unique_islands; + + remove_all_points = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_editing_mode_cache.size())+")")); + + m_imgui->text(" "); // vertical gap + + bool apply_changes = m_imgui->button(_(L("Apply changes"))); + if (apply_changes) { + m_model_object->sla_support_points = m_editing_mode_cache; + m_editing_mode = false; + force_refresh = true; + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } + ImGui::SameLine(); + bool discard_changes = m_imgui->button(_(L("Cancel"))); + if (discard_changes) { + m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode = false; + + m_grabbers.clear(); + for (const sla::SupportPoint& point : m_editing_mode_cache) { + m_grabbers.push_back(Grabber()); + m_grabbers.back().center = point.pos.cast(); + } + + force_refresh = true; + } } else { + //m_imgui->text("Some settings could"); + //m_imgui->text("be exposed here..."); + m_imgui->text(""); + m_imgui->text(""); + + m_imgui->text(""); + bool generate =m_imgui->button(_(L("Auto-generate points"))); force_refresh |= generate; - if (generate) + if (generate) { + m_model_object->sla_support_points.clear(); + m_grabbers.clear(); + m_editing_mode_cache.clear(); 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(); - - if (remove_all_clicked) { - delete_current_grabber(true); - if (first_run) { - first_run = false; - goto RENDER_AGAIN; + } + ImGui::SameLine(); + bool editing_clicked = m_imgui->button("Editing"); + if (editing_clicked) { + m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode = true; } } - if (remove_all_clicked || force_refresh) { + m_imgui->end(); + + if (m_editing_mode != old_editing_state) { // user just toggled between editing/non-editing mode + m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode); + force_refresh = true; + } + + + if (remove_all_points) { + force_refresh = true; + delete_current_grabber(true); + /*if (first_run) { + first_run = false; + goto RENDER_AGAIN; + }*/ + } + + if (force_refresh) { m_parent.reload_scene(true); - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } } #endif // ENABLE_IMGUI @@ -2174,6 +2233,20 @@ std::string GLGizmoSlaSupports::on_get_name() const return L("SLA Support Points [L]"); } +void GLGizmoSlaSupports::on_set_state() +{ + if (m_state == On) { + if (is_mesh_update_necessary()) + update_mesh(); + + m_parent.toggle_model_objects_visibility(false); + if (m_model_object) + m_parent.toggle_model_objects_visibility(true, m_model_object); + } + if (m_state == Off) + m_parent.toggle_model_objects_visibility(true); +} + // GLGizmoCut diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 7e2a3bb0b..06ef4b0d2 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -485,13 +485,10 @@ private: bool m_lock_unique_islands = false; bool m_editing_mode = false; float m_new_point_head_diameter = 0.4f; + std::vector m_editing_mode_cache; protected: - void on_set_state() override { - if (m_state == On && is_mesh_update_necessary()) { - update_mesh(); - } - } + void on_set_state() override; #if ENABLE_IMGUI virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) override; From 24a0fdb8449e2dd014a27fff2458febad50b05cf Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 4 Feb 2019 09:50:25 +0100 Subject: [PATCH 03/28] SLA support points generator now uses precalculated aabb tree --- src/libslic3r/SLA/SLAAutoSupports.cpp | 32 ++++++++++++------------- src/libslic3r/SLA/SLAAutoSupports.hpp | 3 +-- src/libslic3r/SLA/SLACommon.hpp | 3 ++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 1 + 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 1d147be72..c853e8756 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -44,44 +44,44 @@ 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) +: m_config(config), m_emesh(emesh), m_throw_on_cancel(throw_on_cancel) { process(slices, heights); 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); + //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); + + sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); + sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); + + bool up = hit_up.face() != -1; + bool down = hit_down.face() != -1; 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(); + sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; + //int fid = hit.face(); + //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(); + + p = p + (hit.distance() * hit.direction()).cast(); } } - - - void SLAAutoSupports::process(const std::vector& slices, const std::vector& heights) { std::vector> islands; diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 8a262bbe3..43399f4ae 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -55,8 +55,7 @@ private: std::function m_throw_on_cancel; - const Eigen::MatrixXd& m_V; - const Eigen::MatrixXi& m_F; + const sla::EigenMesh3D& m_emesh; }; diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 62dd6f053..b7e909600 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -71,12 +71,13 @@ public: int m_face_id = -1; const EigenMesh3D& m_mesh; Vec3d m_dir; + Vec3d m_source; inline hit_result(const EigenMesh3D& em): m_mesh(em) {} friend class EigenMesh3D; public: inline double distance() const { return m_t; } - + inline const Vec3d& direction() const { return m_dir; } inline int face() const { return m_face_id; } inline Vec3d normal() const { diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index d3af1eac8..4088217fe 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -163,6 +163,7 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const hit_result ret(*this); ret.m_t = double(hit.t); ret.m_dir = dir; + ret.m_source = s; if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id; return ret; From fab3025107f3a0e3ec9db01f579e1457c6d9de12 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 4 Feb 2019 10:56:49 +0100 Subject: [PATCH 04/28] SLA support points - minor adjustments --- src/libslic3r/SLA/SLAAutoSupports.cpp | 15 +++++++++------ src/slic3r/GUI/GLGizmo.cpp | 13 +++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index c853e8756..ddf280471 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -111,8 +111,11 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: m_structures_new.back().structures_below.push_back(&s); coord_t centroids_dist = (bottom->contour.centroid() - polygon.contour.centroid()).norm(); - float mult = std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * (float)(centroids_dist * centroids_dist) / (float)bottom->area())); - s.supports_force *= mult; + + // Penalization resulting from centroid offset: + s.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * (float)(centroids_dist * centroids_dist) / (float)bottom->area())); + + // Penalization resulting from increasing polygon area: s.supports_force *= std::min(1.f, 20.f * ((float)bottom->area() / (float)polygon.area())); } } @@ -165,10 +168,10 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: } e = diff_ex(ExPolygons{*s.polygon}, e); + + // Penalization resulting from large diff from the last layer: s.supports_force /= std::max(1., (layer_height / 0.3f) * (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()) @@ -215,7 +218,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct std::vector island_new_points; const BoundingBox& bb = get_extents(island); - const int refused_limit = 30; + const int refused_limit = 30 * ((float)bb.size()(0)*bb.size()(1) / (float)island.area()); int refused_points = 0; while (refused_points < refused_limit) { Point out; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 6522287b5..4b0b917e9 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1941,6 +1941,7 @@ void GLGizmoSlaSupports::update_mesh() // we'll now reload Grabbers (selection might have changed): m_grabbers.clear(); + m_editing_mode_cache = m_model_object->sla_support_points; for (const sla::SupportPoint& point : m_editing_mode_cache) { m_grabbers.push_back(Grabber()); @@ -2044,7 +2045,7 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) // wxGetApp().plater()->reslice(); } } - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + //m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection) @@ -2203,17 +2204,17 @@ RENDER_AGAIN: if (remove_all_points) { - force_refresh = true; + force_refresh = false; + m_parent.reload_scene(true); delete_current_grabber(true); - /*if (first_run) { + if (first_run) { first_run = false; goto RENDER_AGAIN; - }*/ + } } - if (force_refresh) { + if (force_refresh) m_parent.reload_scene(true); - } } #endif // ENABLE_IMGUI From 9dff44a8ad1aa2b77d3d1c4afb2cecd8effdfc62 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 4 Feb 2019 12:33:59 +0100 Subject: [PATCH 05/28] SLA support gizmo does not use Grabber class anymore --- src/libslic3r/SLA/SLACommon.hpp | 1 - src/libslic3r/SLA/SLASupportTreeIGL.cpp | 1 - src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/GLGizmo.cpp | 97 ++++++++++--------------- src/slic3r/GUI/GLGizmo.hpp | 4 +- 5 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index b7e909600..293172346 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -71,7 +71,6 @@ public: int m_face_id = -1; const EigenMesh3D& m_mesh; Vec3d m_dir; - Vec3d m_source; inline hit_result(const EigenMesh3D& em): m_mesh(em) {} friend class EigenMesh3D; public: diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 4088217fe..d3af1eac8 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -163,7 +163,6 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const hit_result ret(*this); ret.m_t = double(hit.t); ret.m_dir = dir; - ret.m_source = s; if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id; return ret; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2ee322ebe..b70ba60e0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3411,7 +3411,7 @@ void GLCanvas3D::Gizmos::delete_current_grabber(bool delete_all) GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->delete_current_grabber(delete_all); + reinterpret_cast(it->second)->delete_current_point(delete_all); } void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selection) const diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 4b0b917e9..fc7350207 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1799,7 +1799,8 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G } } } - m_editing_mode_cache = m_model_object->sla_support_points; // make a copy of ModelObject's support points + if (m_old_model_object != m_model_object) + m_editing_mode_cache = m_model_object->sla_support_points; // make a copy of ModelObject's support points if (m_state == On) { m_parent.toggle_model_objects_visibility(false); m_parent.toggle_model_objects_visibility(true, m_model_object); @@ -1812,21 +1813,7 @@ void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const ::glEnable(GL_BLEND); ::glEnable(GL_DEPTH_TEST); - for (unsigned int i=0; i glmatrix; + Eigen::Matrix glmatrix; glGetFloatv (GL_MODELVIEW_MATRIX, glmatrix.data()); - Eigen::Matrix grabber_pos; + Eigen::Matrix point_pos_4d; for (int j=0; j<3; ++j) - grabber_pos(j) = m_grabbers[i].center(j); - grabber_pos[3] = 1.f; - Eigen::Matrix grabber_world_position = glmatrix * grabber_pos; + point_pos_4d(j) = point_pos(j); + point_pos_4d[3] = 1.f; + Eigen::Matrix grabber_world_position = glmatrix * point_pos_4d; - if (!picking && (m_hover_id == i)) + if (!picking && (m_hover_id == i)) // point is in hover state { - 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]; + render_color[0] = 0.f; + render_color[1] = 1.0f; + render_color[2] = 1.0f; + } + else { + if (picking) { + render_color[0] = 1.0f; + render_color[1] = 1.0f; + render_color[2] = picking_color_component(i); + } + else { // normal rendering + bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].is_new_island; + if (m_editing_mode) { + render_color[0] = supports_new_island ? 0.f : 1.f; + render_color[1] = 0.f; + render_color[2] = supports_new_island ? 1.f : 0.f; + } + else + for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f; + } } - else - ::memcpy((void*)render_color, (const void*)m_grabbers[i].color, 3 * sizeof(float)); ::glColor3fv(render_color); ::glPushMatrix(); @@ -1939,14 +1938,8 @@ void GLGizmoSlaSupports::update_mesh() m_AABB = igl::AABB(); m_AABB.init(m_V, m_F); - // we'll now reload Grabbers (selection might have changed): - m_grabbers.clear(); + // we'll now reload support points (selection might have changed): m_editing_mode_cache = m_model_object->sla_support_points; - - for (const sla::SupportPoint& point : m_editing_mode_cache) { - m_grabbers.push_back(Grabber()); - m_grabbers.back().center = point.pos.cast(); - } } Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) @@ -2007,13 +2000,10 @@ void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position) Vec3f new_pos; try { - new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new grabber in that case + new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case } catch (...) { return; } - m_grabbers.push_back(Grabber()); - m_grabbers.back().center = new_pos.cast(); - m_editing_mode_cache.emplace_back(new_pos, m_new_point_head_diameter, false); // This should trigger the support generation @@ -2022,13 +2012,12 @@ void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position) m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) +void GLGizmoSlaSupports::delete_current_point(bool delete_all) { if (!m_editing_mode && !delete_all) return; if (delete_all) { - m_grabbers.clear(); m_editing_mode_cache.clear(); // This should trigger the support generation @@ -2037,7 +2026,6 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) else if (m_hover_id != -1) { if (!m_editing_mode_cache[m_hover_id].is_new_island || !m_lock_unique_islands) { - m_grabbers.erase(m_grabbers.begin() + m_hover_id); m_editing_mode_cache.erase(m_editing_mode_cache.begin() + m_hover_id); m_hover_id = -1; @@ -2056,7 +2044,6 @@ void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Sel 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_editing_mode_cache[m_hover_id].pos = new_pos; m_editing_mode_cache[m_hover_id].is_new_island = false; // Do not update immediately, wait until the mouse is released. @@ -2161,13 +2148,6 @@ RENDER_AGAIN: if (discard_changes) { m_editing_mode_cache = m_model_object->sla_support_points; m_editing_mode = false; - - m_grabbers.clear(); - for (const sla::SupportPoint& point : m_editing_mode_cache) { - m_grabbers.push_back(Grabber()); - m_grabbers.back().center = point.pos.cast(); - } - force_refresh = true; } } @@ -2183,7 +2163,6 @@ RENDER_AGAIN: force_refresh |= generate; if (generate) { m_model_object->sla_support_points.clear(); - m_grabbers.clear(); m_editing_mode_cache.clear(); wxGetApp().plater()->reslice(); } @@ -2206,7 +2185,7 @@ RENDER_AGAIN: if (remove_all_points) { force_refresh = false; m_parent.reload_scene(true); - delete_current_grabber(true); + delete_current_point(true); if (first_run) { first_run = false; goto RENDER_AGAIN; diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 06ef4b0d2..060be6fbb 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -464,7 +464,7 @@ public: virtual ~GLGizmoSlaSupports(); void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); void clicked_on_object(const Vec2d& mouse_position); - void delete_current_grabber(bool delete_all); + void delete_current_point(bool delete_all); private: bool on_init(); @@ -472,7 +472,7 @@ private: virtual void on_render(const GLCanvas3D::Selection& selection) const; virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; - void render_grabbers(const GLCanvas3D::Selection& selection, bool picking = false) const; + void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const; bool is_mesh_update_necessary() const; void update_mesh(); From 0e8b526af378d7ffffc580aefc1be101becce94b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 6 Feb 2019 10:57:45 +0100 Subject: [PATCH 06/28] Vojtech's optimizations --- src/libslic3r/SLA/SLAAutoSupports.cpp | 181 +++++++++++++------------- src/libslic3r/SLA/SLAAutoSupports.hpp | 59 +++++++-- src/libslic3r/SLA/SLACommon.hpp | 2 +- 3 files changed, 137 insertions(+), 105 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index ddf280471..c87f985da 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -1,6 +1,8 @@ #include "igl/random_points_on_mesh.h" #include "igl/AABB.h" +#include + #include "SLAAutoSupports.hpp" #include "Model.hpp" #include "ExPolygon.hpp" @@ -58,42 +60,52 @@ void SLAAutoSupports::project_onto_mesh(std::vector& points) 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); + // Use a reasonable granularity to account for the worker thread synchronization cost. + tbb::parallel_for(tbb::blocked_range(0, points.size(), 64), + [this, &points](const tbb::blocked_range& range) { + for (size_t point_id = range.begin(); point_id < range.end(); ++ point_id) { + if ((point_id % 16) == 0) + // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. + m_throw_on_cancel(); + Vec3f& p = points[point_id].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); - sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); - sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); + sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); + sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); - bool up = hit_up.face() != -1; - bool down = hit_down.face() != -1; + bool up = hit_up.face() != -1; + bool down = hit_down.face() != -1; - if (!up && !down) - continue; + if (!up && !down) + continue; - sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; - //int fid = hit.face(); - //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(); + sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; + //int fid = hit.face(); + //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(); - p = p + (hit.distance() * hit.direction()).cast(); - } + p = p + (hit.distance() * hit.direction()).cast(); + } + }); } void SLAAutoSupports::process(const std::vector& slices, const std::vector& heights) { std::vector> islands; + std::vector structures_old; + std::vector structures_new; for (unsigned int i = 0; i2 ? heights[i-3] : heights[0]-(heights[1]-heights[0])); const float layer_height = (i!=0 ? heights[i]-heights[i-1] : heights[0]); - const float safe_angle = 5.f * (M_PI/180.f); // smaller number - less supports - const float offset = scale_(layer_height / std::tan(safe_angle)); + const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports + const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); // FIXME: calculate actual pixel area from printer config: //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // @@ -101,87 +113,76 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: // 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) + float area = float(polygon.area() * SCALING_FACTOR * SCALING_FACTOR); + if (area < 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(); - + //FIXME this is not a correct centroid of a polygon with holes. + structures_new.emplace_back(polygon, get_extents(polygon.contour), Slic3r::unscale(polygon.contour.centroid()).cast(), area, height); + Structure& top = structures_new.back(); + //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. + // At least it is now using a bounding box check for pre-filtering. + for (Structure& bottom : structures_old) + if (top.overlaps(bottom)) { + top.structures_below.push_back(&bottom); + float centroids_dist = (bottom.centroid - top.centroid).norm(); // Penalization resulting from centroid offset: - s.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * (float)(centroids_dist * centroids_dist) / (float)bottom->area())); - +// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); + bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, 80.f * centroids_dist * centroids_dist / bottom.area)); // Penalization resulting from increasing polygon area: - s.supports_force *= std::min(1.f, 20.f * ((float)bottom->area() / (float)polygon.area())); + bottom.supports_force *= std::min(1.f, 20.f * bottom.area / top.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 (const Structure& below : structures_old) { + std::vector above_list; + float above_area = 0.f; + for (Structure& new_str : structures_new) + for (const Structure* below1 : new_str.structures_below) + if (&below == below1) { + above_list.push_back(&new_str); + above_area += above_list.back()->area; } - for (Structure* child : children) - child->supports_force += (old_str.supports_force/children_area) * (child->polygon->area() * pow(SCALING_FACTOR, 2)); + for (Structure* above : above_list) + above->supports_force += below.supports_force * above->area / above_area; } // 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 + for (Structure& s : structures_new) { + if (s.structures_below.empty()) // completely new island - needs support no doubt uniformly_cover(*s.polygon, s, true); - } - else { + 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) + for (const ExPolygon& p : diff_ex(to_polygons(*s.polygon), offset(s.expolygons_below(), between_layers_offset))) + //FIXME is it an island point or not? Vojtech thinks it is. 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); - + for (Structure& s : structures_new) { + // Areas not supported by the areas below. + ExPolygons e = diff_ex(to_polygons(*s.polygon), s.polygons_below()); + float e_area = 0.f; + for (const ExPolygon &ex : e) + e_area += float(ex.area()); // Penalization resulting from large diff from the last layer: - s.supports_force /= std::max(1., (layer_height / 0.3f) * (e_area / (s.polygon->area()*SCALING_FACTOR*SCALING_FACTOR))); +// s.supports_force /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); + s.supports_force /= std::max(1.f, 0.17f * (e_area * float(SCALING_FACTOR * SCALING_FACTOR)) / s.area); - if ( (s.polygon->area() * pow(SCALING_FACTOR, 2)) * m_config.tear_pressure > s.supports_force) { + if (s.area * m_config.tear_pressure > s.supports_force) { + //FIXME Don't calculate area inside the compare function! + //FIXME Cover until the force deficit is covered. Cover multiple areas, sort by decreasing area. 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()) + //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. 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(); + structures_old = std::move(structures_new); + structures_new.clear(); m_throw_on_cancel(); @@ -195,15 +196,6 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: } } - -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)); @@ -212,14 +204,18 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct // We will cover the island another way. // For now we'll just place the points randomly not too close to the others. + //FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon. 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 * ((float)bb.size()(0)*bb.size()(1) / (float)island.area()); + const BoundingBox bb = get_extents(island); + const int refused_limit = (int)floor(30.f * ((float)bb.size()(0)*bb.size()(1) / (float)island.area()) + 0.5f); int refused_points = 0; + //FIXME this is very inefficient (may be blind) for long narrow polygons (thin crescent, thin ring). Use some search structure: Triangulate the polygon first? + //FIXME use a low discrepancy sequence, Poisson sampling. + // Poisson sampling (adapt from 3D to 2D by triangulation): https://github.com/zewt/maya-implicit-skinning/blob/master/src/meshes/vcg_lib/utils_sampling.cpp while (refused_points < refused_limit) { Point out; if (refused_points == 0 && island_new_points.empty()) // first iteration @@ -227,15 +223,17 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct 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); + Vec3d unscaled_out = unscale(out(0), out(1), 0); bool add_it = true; if (!island.contour.contains(out)) add_it = false; else for (const Polygon& hole : island.holes) - if (hole.contains(out)) + if (hole.contains(out)) { add_it = false; + break; + } if (add_it) { for (const Vec3d& p : island_new_points) { @@ -254,14 +252,15 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct ++refused_points; } - for (const Vec3d& p : island_new_points) - add_point(Point(scale_(p.x()), scale_(p.y())), structure, is_new_island); + for (const Vec3d& p : island_new_points) { + m_output.emplace_back(float(p(0)), float(p(1)), structure.height, 0.4f, is_new_island); + structure.supports_force += m_config.support_force; + } } #ifdef SLA_AUTOSUPPORTS_DEBUG -void SLAAutoSupports::output_structures() const +void SLAAutoSupports::output_structures(const std::vector& structures) { - const std::vector& structures = m_structures_new; for (unsigned int i=0 ; i& slices, @@ -27,32 +27,65 @@ public: private: 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; - } + Structure(const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) : polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h) +#ifdef SLA_AUTOSUPPORTS_DEBUG + , unique_id(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch())) +#endif /* SLA_AUTOSUPPORTS_DEBUG */ + {} const ExPolygon* polygon = nullptr; + const BoundingBox bbox; + const Vec2f centroid = Vec3f::Zero(); + const float area = 0.f; std::vector structures_below; float height = 0; + // How well is this ExPolygon held to the print base? + // Positive number, the higher the better. float supports_force = 0.f; +#ifdef SLA_AUTOSUPPORTS_DEBUG std::chrono::milliseconds unique_id; +#endif /* SLA_AUTOSUPPORTS_DEBUG */ + + bool overlaps(const Structure &rhs) const { return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); } + float area_below() const { + float area = 0.f; + for (const Structure *below : this->structures_below) + area += below->area; + return area; + } + Polygons polygons_below() const { + size_t cnt = 0; + for (const Structure *below : this->structures_below) + cnt += 1 + below->polygon->holes.size(); + Polygons out; + out.reserve(cnt); + for (const Structure *below : this->structures_below) { + out.emplace_back(below->polygon->contour); + append(out, below->polygon->holes); + } + return out; + } + ExPolygons expolygons_below() const { + ExPolygons out; + out.reserve(this->structures_below.size()); + for (const Structure *below : this->structures_below) + out.emplace_back(*below->polygon); + return out; + } }; - 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; +#ifdef SLA_AUTOSUPPORTS_DEBUG + static void output_expolygons(const ExPolygons& expolys, const std::string &filename); + static void output_structures(const std::vector &structures); +#endif // SLA_AUTOSUPPORTS_DEBUG std::function m_throw_on_cancel; const sla::EigenMesh3D& m_emesh; diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 293172346..dcf991aec 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -28,7 +28,7 @@ struct SupportPoint { 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)) {} + pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4) != 0.f) {} bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; } }; From 906e66ea800193a5b0dbc123d8b081eccc05eaee Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 6 Feb 2019 11:09:00 +0100 Subject: [PATCH 07/28] Tiny fix of previous commit --- src/libslic3r/SLA/SLAAutoSupports.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 2ad1d0397..fb5aceb4a 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -37,7 +37,7 @@ private: {} const ExPolygon* polygon = nullptr; const BoundingBox bbox; - const Vec2f centroid = Vec3f::Zero(); + const Vec2f centroid = Vec2f::Zero(); const float area = 0.f; std::vector structures_below; float height = 0; From fb6f3d843102ec1a5b5ad52e5972bba72da68e82 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 6 Feb 2019 14:18:04 +0100 Subject: [PATCH 08/28] Send EVT_SLICING_UPDATE with RELOAD_SLA_SUPPORT_POINTS once object's support points are calculated by the backend. --- src/libslic3r/PrintBase.hpp | 5 +++-- src/libslic3r/SLAPrint.cpp | 3 +++ src/slic3r/GUI/Plater.cpp | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 84d04d26f..f67ec4b72 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -244,8 +244,9 @@ public: // Bitmap of flags. enum FlagBits { DEFAULT, - NO_RELOAD_SCENE = 0, - RELOAD_SCENE = 1, + NO_RELOAD_SCENE = 0, + RELOAD_SCENE = 1 << 1, + RELOAD_SLA_SUPPORT_POINTS = 1 << 2, }; // Bitmap of FlagBits unsigned int flags; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 423ee123d..e00437862 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -552,6 +552,9 @@ void SLAPrint::process() BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " << po.m_supportdata->support_points.rows(); + + // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports + report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); } else { // There are some points on the front-end, no calculation will be done. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1968dad69..709e9c9e5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2229,6 +2229,9 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) break; } } + if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS) { + // Update SLA gizmo + } } void Plater::priv::on_slicing_completed(wxCommandEvent &) From 9fc75d7b34e0e1ca93f8c495d52a2d5d0e6d4959 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 6 Feb 2019 15:16:25 +0100 Subject: [PATCH 09/28] SLA support points (better backend->frontend synchronization and more) - backend to frontend data synchronization to inform the gizmo that new points have been generated - fixed the inadvertent cancellation of background processing caused by the SLA gizmo touching frontend data during the process - inactive instances of the object are hidden when the SLA gizmo is active - fix of imgui combobox rendering --- src/slic3r/GUI/GLCanvas3D.cpp | 5 +++-- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/GLGizmo.cpp | 14 ++++++++------ src/slic3r/GUI/GLGizmo.hpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 10 +++++----- src/slic3r/GUI/Plater.cpp | 1 + 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 210d00eef..883d08314 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4238,10 +4238,11 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible) m_render_sla_auxiliaries = visible; } -void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo) +void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) { for (GLVolume* vol : m_volumes.volumes) { - if (mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + if (mo == nullptr + | (m_model->objects[vol->composite_id.object_id] == mo && vol->composite_id.instance_id == instance_idx)) vol->is_active = visible; } if (visible && !mo) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8e4acd724..9f341083b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -952,7 +952,7 @@ public: int check_volumes_outside_state() const; void toggle_sla_auxiliaries_visibility(bool visible); - void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr); + void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void set_config(const DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index fc7350207..563ac3259 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1782,28 +1782,30 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G if (selection.is_empty()) m_old_instance_id = -1; + m_active_instance = selection.get_instance_idx(); + if ((model_object != nullptr) && selection.is_from_single_instance()) { if (is_mesh_update_necessary()) update_mesh(); // If there are no points, let's ask the backend if it calculated some. - if (model_object->sla_support_points.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) { + if (m_editing_mode_cache.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) { 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.emplace_back(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), + m_editing_mode_cache.emplace_back(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), points(i, 3), points(i, 4)); break; } } } - if (m_old_model_object != m_model_object) - m_editing_mode_cache = m_model_object->sla_support_points; // make a copy of ModelObject's support points + if (m_model_object != m_old_model_object) + m_editing_mode = false; if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_model_object); + m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); } } } @@ -2221,7 +2223,7 @@ void GLGizmoSlaSupports::on_set_state() m_parent.toggle_model_objects_visibility(false); if (m_model_object) - m_parent.toggle_model_objects_visibility(true, m_model_object); + m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); } if (m_state == Off) m_parent.toggle_model_objects_visibility(true); diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 060be6fbb..f5ddbd76f 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -442,6 +442,7 @@ class GLGizmoSlaSupports : public GLGizmoBase private: ModelObject* m_model_object = nullptr; ModelObject* m_old_model_object = nullptr; + int m_active_instance = -1; int m_old_instance_id = -1; Vec3f unproject_on_mesh(const Vec2d& mouse_pos); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 4f5cbf749..3ce187a44 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -170,17 +170,17 @@ void ImGuiWrapper::text(const wxString &label) void ImGuiWrapper::combo(const wxString& label, const std::vector& options, wxString& selection) { - const char* selection_u8 = into_u8(selection).c_str(); + std::string selection_u8 = into_u8(selection); // this is to force the label to the left of the widget: text(label); ImGui::SameLine(); - if (ImGui::BeginCombo("", selection_u8)) { + if (ImGui::BeginCombo("", selection_u8.c_str())) { 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)) + std::string option_u8 = into_u8(option); + bool is_selected = (selection_u8.empty()) ? false : (option_u8 == selection_u8); + if (ImGui::Selectable(option_u8.c_str(), is_selected)) selection = option_u8; } ImGui::EndCombo(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 709e9c9e5..671b6499f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2231,6 +2231,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) } if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS) { // Update SLA gizmo + q->canvas3D()->reload_scene(true); } } From 4357c80793c91a6be394417fa5acba023ae814dc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 6 Feb 2019 17:20:54 +0100 Subject: [PATCH 10/28] SLA gizmo - fixed hiding of objects and instances --- src/slic3r/GUI/3DScene.cpp | 4 ++-- src/slic3r/GUI/3DScene.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 21 +++++++++++---------- src/slic3r/GUI/GLGizmo.cpp | 4 +++- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6b1df6ab3..67d8dd4a2 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -793,7 +793,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool glsafe(::glDisable(GL_BLEND)); } -void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) const +void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, std::function filter_func) const { glsafe(glEnable(GL_BLEND)); glsafe(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -805,7 +805,7 @@ void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) glsafe(glEnableClientState(GL_VERTEX_ARRAY)); glsafe(glEnableClientState(GL_NORMAL_ARRAY)); - GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, std::function()); + GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func); for (GLVolumeWithZ& volume : to_render) { volume.first->set_render_color(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 2732b5a13..f6ccb7646 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -456,7 +456,7 @@ public: // Render the volumes by OpenGL. void render_VBOs(ERenderType type, bool disable_cullface, std::function filter_func = std::function()) const; - void render_legacy(ERenderType type, bool disable_cullface) const; + void render_legacy(ERenderType type, bool disable_cullface, std::function filter_func = std::function()) const; // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 883d08314..76c0337d1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4241,8 +4241,8 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible) void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) { for (GLVolume* vol : m_volumes.volumes) { - if (mo == nullptr - | (m_model->objects[vol->composite_id.object_id] == mo && vol->composite_id.instance_id == instance_idx)) + if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)) vol->is_active = visible; } if (visible && !mo) @@ -4814,7 +4814,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (it != model_volume_state.end() && it->geometry_id == key.geometry_id) mvs = &(*it); } - if (mvs == nullptr || force_full_scene_refresh || (volume->composite_id.volume_id < 0 && !m_render_sla_auxiliaries)) { + if (mvs == nullptr || force_full_scene_refresh) { // This GLVolume will be released. if (volume->is_wipe_tower) { // There is only one wipe tower. @@ -4925,7 +4925,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volumes_count = m_volumes.volumes.size(); for (size_t istep = 0; istep < sla_steps.size(); ++istep) - if (!instances[istep].empty() && m_render_sla_auxiliaries) + if (!instances[istep].empty()) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_use_VBOs && m_initialized); } @@ -6581,7 +6581,9 @@ void GLCanvas3D::_render_objects() const m_layers_editing.render_volumes(*this, this->m_volumes); } else { // do not cull backfaces to show broken geometry, if any - m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled); + m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) { + return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); + }); } m_volumes.render_VBOs(GLVolumeCollection::Transparent, false); m_shader.stop_using(); @@ -6597,7 +6599,9 @@ void GLCanvas3D::_render_objects() const } // do not cull backfaces to show broken geometry, if any - m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled); + m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, [this](const GLVolume& volume) { + return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); + }); m_volumes.render_legacy(GLVolumeCollection::Transparent, false); if (m_use_clipping_planes) @@ -6664,9 +6668,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const unsigned int volume_id = 0; for (GLVolume* vol : m_volumes.volumes) { - if (vol->composite_id.volume_id < 0 && !m_render_sla_auxiliaries) - continue; - if (fake_colors) { // Object picking mode. Render the object with a color encoding the object index. @@ -6681,7 +6682,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const ::glColor4fv(vol->render_color); } - if (!fake_colors || !vol->disabled) + if ((!fake_colors || !vol->disabled) && (vol->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) vol->render(); ++volume_id; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 563ac3259..f9191b69b 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2225,8 +2225,10 @@ void GLGizmoSlaSupports::on_set_state() if (m_model_object) m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); } - if (m_state == Off) + if (m_state == Off) { m_parent.toggle_model_objects_visibility(true); + m_editing_mode = false; + } } From d0553ece0e7b28a0b23d4e66450aca5ab072b9b9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 8 Feb 2019 09:26:48 +0100 Subject: [PATCH 11/28] Extended tesselation wrapper for other 3d and 2d point types. --- src/libslic3r/Point.hpp | 1 + src/libslic3r/Tesselate.cpp | 56 ++++++++++++++++++++++++++++++---- src/libslic3r/Tesselate.hpp | 8 +++-- src/libslic3r/TriangleMesh.cpp | 4 +-- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++--- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index d456fb9c6..a03e6f436 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -22,6 +22,7 @@ typedef Point Vector; // Vector types with a fixed point coordinate base type. typedef Eigen::Matrix Vec2crd; typedef Eigen::Matrix Vec3crd; +typedef Eigen::Matrix Vec2i; typedef Eigen::Matrix Vec3i; typedef Eigen::Matrix Vec2i64; typedef Eigen::Matrix Vec3i64; diff --git a/src/libslic3r/Tesselate.cpp b/src/libslic3r/Tesselate.cpp index f4ce0703d..febb3d0e7 100644 --- a/src/libslic3r/Tesselate.cpp +++ b/src/libslic3r/Tesselate.cpp @@ -20,7 +20,7 @@ public: gluDeleteTess(m_tesselator); } - Pointf3s tesselate(const ExPolygon &expoly, double z_, bool flipped_) + std::vector tesselate3d(const ExPolygon &expoly, double z_, bool flipped_) { m_z = z_; m_flipped = flipped_; @@ -56,7 +56,7 @@ public: return std::move(m_output_triangles); } - Pointf3s tesselate(const ExPolygons &expolygons, double z_, bool flipped_) + std::vector tesselate3d(const ExPolygons &expolygons, double z_, bool flipped_) { m_z = z_; m_flipped = flipped_; @@ -189,16 +189,60 @@ private: bool m_flipped; }; -Pointf3s triangulate_expolygons_3df(const ExPolygon &poly, coordf_t z, bool flip) +std::vector triangulate_expolygon_3d(const ExPolygon &poly, coordf_t z, bool flip) { GluTessWrapper tess; - return tess.tesselate(poly, z, flip); + return tess.tesselate3d(poly, z, flip); } -Pointf3s triangulate_expolygons_3df(const ExPolygons &polys, coordf_t z, bool flip) +std::vector triangulate_expolygons_3d(const ExPolygons &polys, coordf_t z, bool flip) { GluTessWrapper tess; - return tess.tesselate(polys, z, flip); + return tess.tesselate3d(polys, z, flip); +} + +std::vector triangulate_expolygon_2d(const ExPolygon &poly, bool flip) +{ + GluTessWrapper tess; + std::vector triangles = tess.tesselate3d(poly, 0, flip); + std::vector out; + out.reserve(triangles.size()); + for (const Vec3d &pt : triangles) + out.emplace_back(pt.x(), pt.y()); + return out; +} + +std::vector triangulate_expolygons_2d(const ExPolygons &polys, bool flip) +{ + GluTessWrapper tess; + std::vector triangles = tess.tesselate3d(polys, 0, flip); + std::vector out; + out.reserve(triangles.size()); + for (const Vec3d &pt : triangles) + out.emplace_back(pt.x(), pt.y()); + return out; +} + +std::vector triangulate_expolygon_2f(const ExPolygon &poly, bool flip) +{ + GluTessWrapper tess; + std::vector triangles = tess.tesselate3d(poly, 0, flip); + std::vector out; + out.reserve(triangles.size()); + for (const Vec3d &pt : triangles) + out.emplace_back(float(pt.x()), float(pt.y())); + return out; +} + +std::vector triangulate_expolygons_2f(const ExPolygons &polys, bool flip) +{ + GluTessWrapper tess; + std::vector triangles = tess.tesselate3d(polys, 0, flip); + std::vector out; + out.reserve(triangles.size()); + for (const Vec3d &pt : triangles) + out.emplace_back(float(pt.x()), float(pt.y())); + return out; } } // namespace Slic3r diff --git a/src/libslic3r/Tesselate.hpp b/src/libslic3r/Tesselate.hpp index ed12df888..02e86eb33 100644 --- a/src/libslic3r/Tesselate.hpp +++ b/src/libslic3r/Tesselate.hpp @@ -10,8 +10,12 @@ namespace Slic3r { class ExPolygon; typedef std::vector ExPolygons; -extern Pointf3s triangulate_expolygons_3df(const ExPolygon &poly, coordf_t z = 0, bool flip = false); -extern Pointf3s triangulate_expolygons_3df(const ExPolygons &polys, coordf_t z = 0, bool flip = false); +extern std::vector triangulate_expolygon_3d (const ExPolygon &poly, coordf_t z = 0, bool flip = false); +extern std::vector triangulate_expolygons_3d(const ExPolygons &polys, coordf_t z = 0, bool flip = false); +extern std::vector triangulate_expolygon_2d (const ExPolygon &poly, bool flip = false); +extern std::vector triangulate_expolygons_2d(const ExPolygons &polys, bool flip = false); +extern std::vector triangulate_expolygon_2f (const ExPolygon &poly, bool flip = false); +extern std::vector triangulate_expolygons_2f(const ExPolygons &polys, bool flip = false); } // namespace Slic3r diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index b93ce12b5..0784b44f2 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1781,7 +1781,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating upper part"; ExPolygons section; this->make_expolygons_simple(upper_lines, §ion); - Pointf3s triangles = triangulate_expolygons_3df(section, z, true); + Pointf3s triangles = triangulate_expolygons_3d(section, z, true); stl_facet facet; facet.normal = stl_normal(0, 0, -1.f); for (size_t i = 0; i < triangles.size(); ) { @@ -1795,7 +1795,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating lower part"; ExPolygons section; this->make_expolygons_simple(lower_lines, §ion); - Pointf3s triangles = triangulate_expolygons_3df(section, z, false); + Pointf3s triangles = triangulate_expolygons_3d(section, z, false); stl_facet facet; facet.normal = stl_normal(0, 0, -1.f); for (size_t i = 0; i < triangles.size(); ) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 76c0337d1..63dcd950b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6833,20 +6833,20 @@ void GLCanvas3D::_render_sla_slices() const { // calculate model bottom cap if (bottom_obj_triangles.empty() && (it_min_z->second.model_slices_idx < model_slices.size())) - bottom_obj_triangles = triangulate_expolygons_3df(model_slices[it_min_z->second.model_slices_idx], min_z, true); + bottom_obj_triangles = triangulate_expolygons_3d(model_slices[it_min_z->second.model_slices_idx], min_z, true); // calculate support bottom cap if (bottom_sup_triangles.empty() && (it_min_z->second.support_slices_idx < support_slices.size())) - bottom_sup_triangles = triangulate_expolygons_3df(support_slices[it_min_z->second.support_slices_idx], min_z, true); + bottom_sup_triangles = triangulate_expolygons_3d(support_slices[it_min_z->second.support_slices_idx], min_z, true); } if (it_max_z != index.end()) { // calculate model top cap if (top_obj_triangles.empty() && (it_max_z->second.model_slices_idx < model_slices.size())) - top_obj_triangles = triangulate_expolygons_3df(model_slices[it_max_z->second.model_slices_idx], max_z, false); + top_obj_triangles = triangulate_expolygons_3d(model_slices[it_max_z->second.model_slices_idx], max_z, false); // calculate support top cap if (top_sup_triangles.empty() && (it_max_z->second.support_slices_idx < support_slices.size())) - top_sup_triangles = triangulate_expolygons_3df(support_slices[it_max_z->second.support_slices_idx], max_z, false); + top_sup_triangles = triangulate_expolygons_3d(support_slices[it_max_z->second.support_slices_idx], max_z, false); } } From fd4054be7eb73507156f853792e719263cd61b88 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Sat, 9 Feb 2019 07:19:01 +0100 Subject: [PATCH 12/28] First steps on the rectangular selection for the SLA gizmo --- src/slic3r/GUI/GLCanvas3D.cpp | 16 +++-- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/GLGizmo.cpp | 124 ++++++++++++++++++++++++++++------ src/slic3r/GUI/GLGizmo.hpp | 9 ++- 4 files changed, 125 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 76c0337d1..7a0ca3e8f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3430,14 +3430,14 @@ void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const G reinterpret_cast(it->second)->set_sla_support_data(model_object, selection); } -void GLCanvas3D::Gizmos::clicked_on_object(const Vec2d& mouse_position) +void GLCanvas3D::Gizmos::mouse_event(int action, const Vec2d& mouse_position, bool shift_down) { if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->clicked_on_object(mouse_position); + reinterpret_cast(it->second)->mouse_event(action, mouse_position, shift_down); } void GLCanvas3D::Gizmos::delete_current_grabber(bool delete_all) @@ -5396,6 +5396,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _update_gizmos_data(); m_dirty = true; } + else if (evt.LeftDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown()) // inform the gizmo about the event (rectangular selection init) + { + m_gizmos.mouse_event(1, Vec2d(pos(0), pos(1)), evt.ShiftDown()); + } else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) { _update_gizmos_data(); @@ -5579,6 +5583,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } + else if (evt.Dragging() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown()) + { + m_gizmos.mouse_event(3, Vec2d(pos(0), pos(1)), evt.ShiftDown()); + } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) { m_mouse.dragging = true; @@ -5643,12 +5651,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // of the scene with the background processing data should be performed. post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); } - else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_hover_volume_id != -1) + else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports/* && m_hover_volume_id != -1*/) { int id = m_selection.get_object_idx(); if ((id != -1) && (m_model != nullptr)) { - m_gizmos.clicked_on_object(Vec2d(pos(0), pos(1))); + m_gizmos.mouse_event(2, Vec2d(pos(0), pos(1)), evt.ShiftDown()); } } else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9f341083b..6fd4584a6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -789,7 +789,7 @@ private: void set_flattening_data(const ModelObject* model_object); void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); - void clicked_on_object(const Vec2d& mouse_position); + void mouse_event(int action, const Vec2d& mouse_position, bool shift_down); void delete_current_grabber(bool delete_all = false); void render_current_gizmo(const Selection& selection) const; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index f9191b69b..f752e7d94 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1816,13 +1816,49 @@ void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const ::glEnable(GL_DEPTH_TEST); render_points(selection, false); + render_selection_rectangle(); #if !ENABLE_IMGUI render_tooltip_texture(); #endif // not ENABLE_IMGUI + ::glDisable(GL_BLEND); } +void GLGizmoSlaSupports::render_selection_rectangle() const +{ + if (!m_selection_rectangle_active) + return; + + ::glLineWidth(1.5f); + float render_color[3] = {1.f, 0.f, 0.f}; + ::glColor3fv(render_color); + + ::glPushAttrib(GL_TRANSFORM_BIT); // remember current MatrixMode + + ::glMatrixMode(GL_MODELVIEW); // cache modelview matrix and set to identity + ::glPushMatrix(); + ::glLoadIdentity(); + + ::glMatrixMode(GL_PROJECTION); // cache projection matrix and set to identity + ::glPushMatrix(); + ::glLoadIdentity(); + + ::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f); // set projection matrix so that world coords = window coords + + // render the selection rectangle (window coordinates): + ::glBegin(GL_LINE_LOOP); + ::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f); + ::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f); + ::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f); + ::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f); + ::glEnd(); + + ::glPopMatrix(); // restore former projection matrix + ::glMatrixMode(GL_MODELVIEW); + ::glPopMatrix(); // restore former modelview matrix + ::glPopAttrib(); // restore former MatrixMode +} void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const { @@ -1984,34 +2020,82 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) return 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)); } -void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position) +void GLGizmoSlaSupports::mouse_event(int action, const Vec2d& mouse_position, bool shift_down) { if (!m_editing_mode) return; - int instance_id = m_parent.get_selection().get_instance_idx(); - if (m_old_instance_id != instance_id) - { - bool something_selected = (m_old_instance_id != -1); - m_old_instance_id = instance_id; - if (something_selected) + // left down - show the selection rectangle: + if (action == 1 && shift_down) { + m_selection_rectangle_active = true; + m_selection_rectangle_start_corner = mouse_position; + m_selection_rectangle_end_corner = mouse_position; + m_canvas_width = m_parent.get_canvas_size().get_width(); + m_canvas_height = m_parent.get_canvas_size().get_height(); + } + + // dragging the selection rectangle: + if (action == 3 && m_selection_rectangle_active) { + m_selection_rectangle_end_corner = mouse_position; + m_parent.render(); + } + + // mouse up without selection rectangle - place point on the mesh: + if (action == 2 && !m_selection_rectangle_active) { + int instance_id = m_parent.get_selection().get_instance_idx(); + if (m_old_instance_id != instance_id) + { + bool something_selected = (m_old_instance_id != -1); + m_old_instance_id = instance_id; + if (something_selected) + return; + } + if (instance_id == -1) return; + + Vec3f new_pos; + try { + new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case + } + catch (...) { return; } + + m_editing_mode_cache.emplace_back(new_pos, m_new_point_head_diameter, false); } - if (instance_id == -1) - return; - Vec3f new_pos; - try { - new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case + // left up with selection rectangle - select points inside the rectangle: + if (action == 2 && m_selection_rectangle_active) { + const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix(); + GLint viewport[4]; + ::glGetIntegerv(GL_VIEWPORT, viewport); + GLdouble modelview_matrix[16]; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); + GLdouble projection_matrix[16]; + ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); + + 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(); + + // bounding box created from the rectangle corners - will take care of order of the corners + BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); + + for (sla::SupportPoint& support_point : m_editing_mode_cache) { + Vec3f pos = instance_matrix.cast() * support_point.pos; + pos(2) += z_offset; + GLdouble out_x, out_y, out_z; + ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + out_y = m_canvas_height - out_y; + + /*GLfloat depth_comp; + ::glReadPixels( out_x, out_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth_comp); + if (depth_comp > out_z + EPSILON) // the point is obscured by something else + continue;*/ + + if (rectangle.contains(Point(out_x, out_y))) + support_point.head_front_radius = 1.f; + } + m_selection_rectangle_active = false; } - catch (...) { return; } - - m_editing_mode_cache.emplace_back(new_pos, m_new_point_head_diameter, false); - - // This should trigger the support generation - // wxGetApp().plater()->reslice(); - - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } void GLGizmoSlaSupports::delete_current_point(bool delete_all) diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index f5ddbd76f..96dafafdf 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -464,7 +464,7 @@ public: explicit GLGizmoSlaSupports(GLCanvas3D& parent); virtual ~GLGizmoSlaSupports(); void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); - void clicked_on_object(const Vec2d& mouse_position); + void mouse_event(int action, const Vec2d& mouse_position, bool shift_down); void delete_current_point(bool delete_all); private: @@ -473,6 +473,7 @@ private: virtual void on_render(const GLCanvas3D::Selection& selection) const; virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; + void render_selection_rectangle() const; void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const; bool is_mesh_update_necessary() const; void update_mesh(); @@ -488,6 +489,12 @@ private: float m_new_point_head_diameter = 0.4f; std::vector m_editing_mode_cache; + bool m_selection_rectangle_active = false; + Vec2d m_selection_rectangle_start_corner; + Vec2d m_selection_rectangle_end_corner; + int m_canvas_width; + int m_canvas_height; + protected: void on_set_state() override; From 0453caf266abe7400de07905653f2f1ff14ec704 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 11 Feb 2019 08:21:37 +0100 Subject: [PATCH 13/28] SLA gizmo - shift-up and ctrl+a event passing --- src/slic3r/GUI/GLCanvas3D.cpp | 22 +++++++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++- src/slic3r/GUI/GLGizmo.cpp | 18 +++++++++++++++--- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7a0ca3e8f..92078a8b9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5114,6 +5114,7 @@ void GLCanvas3D::bind_event_handlers() m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this); m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this); + m_canvas->Bind(wxEVT_KEY_UP, &GLCanvas3D::on_key_up, this); m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); @@ -5139,6 +5140,7 @@ void GLCanvas3D::unbind_event_handlers() m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this); m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this); + m_canvas->Unbind(wxEVT_KEY_UP, &GLCanvas3D::on_key_up, this); m_canvas->Unbind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); m_canvas->Unbind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); m_canvas->Unbind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); @@ -5182,7 +5184,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) switch (keyCode) { case 'a': case 'A': - case WXK_CONTROL_A: post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); break; + case WXK_CONTROL_A: + if (m_gizmos.get_current_type() == Gizmos::SlaSupports) // Sla gizmo selects all support points + m_gizmos.mouse_event(5); + else + post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); + break; #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #else /* __APPLE__ */ @@ -5240,6 +5247,18 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } } +void GLCanvas3D::on_key_up(wxKeyEvent& evt) +{ + // see include/wx/defs.h enum wxKeyCode + int keyCode = evt.GetKeyCode(); + + // shift has been just released - SLA gizmo might want to close rectangular selection: + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT) { + m_gizmos.mouse_event(2); + m_dirty = true; + } +} + void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { // Ignore the wheel events if the middle button is pressed. @@ -5586,6 +5605,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Dragging() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown()) { m_gizmos.mouse_event(3, Vec2d(pos(0), pos(1)), evt.ShiftDown()); + m_dirty = true; } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6fd4584a6..db630988c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -789,7 +789,7 @@ private: void set_flattening_data(const ModelObject* model_object); void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); - void mouse_event(int action, const Vec2d& mouse_position, bool shift_down); + void mouse_event(int action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false); void delete_current_grabber(bool delete_all = false); void render_current_gizmo(const Selection& selection) const; @@ -1050,6 +1050,7 @@ public: void on_size(wxSizeEvent& evt); void on_idle(wxIdleEvent& evt); void on_char(wxKeyEvent& evt); + void on_key_up(wxKeyEvent& evt); void on_mouse_wheel(wxMouseEvent& evt); void on_timer(wxTimerEvent& evt); void on_mouse(wxMouseEvent& evt); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index f752e7d94..e58eb8500 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1847,12 +1847,17 @@ void GLGizmoSlaSupports::render_selection_rectangle() const ::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f); // set projection matrix so that world coords = window coords // render the selection rectangle (window coordinates): + ::glPushAttrib(GL_ENABLE_BIT); + ::glLineStipple(4, 0xAAAA); + ::glEnable(GL_LINE_STIPPLE); + ::glBegin(GL_LINE_LOOP); ::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f); ::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f); ::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f); ::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f); ::glEnd(); + ::glPopAttrib(); ::glPopMatrix(); // restore former projection matrix ::glMatrixMode(GL_MODELVIEW); @@ -2035,10 +2040,8 @@ void GLGizmoSlaSupports::mouse_event(int action, const Vec2d& mouse_position, bo } // dragging the selection rectangle: - if (action == 3 && m_selection_rectangle_active) { + if (action == 3 && m_selection_rectangle_active) m_selection_rectangle_end_corner = mouse_position; - m_parent.render(); - } // mouse up without selection rectangle - place point on the mesh: if (action == 2 && !m_selection_rectangle_active) { @@ -2096,6 +2099,15 @@ void GLGizmoSlaSupports::mouse_event(int action, const Vec2d& mouse_position, bo } m_selection_rectangle_active = false; } + + if (action == 4) { + // shift has been released + } + + if (action == 5) { + // ctrl+A : select all + std::cout << "select all..." << std::endl; + } } void GLGizmoSlaSupports::delete_current_point(bool delete_all) From ac8f9ab3dce8da344a6e18acb623b5f0db3eb197 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 11 Feb 2019 16:29:03 +0100 Subject: [PATCH 14/28] SLA gizmo improvements - point can be selected - selection can be deleted, head diameter changed - dragging of objects is not allowed with the gizmo being on - added a modal dialog when automatic support generator is triggered --- src/libslic3r/SLA/SLAAutoSupports.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 80 ++++----- src/slic3r/GUI/GLCanvas3D.hpp | 13 +- src/slic3r/GUI/GLGizmo.cpp | 231 ++++++++++++++++---------- src/slic3r/GUI/GLGizmo.hpp | 9 +- 5 files changed, 195 insertions(+), 140 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index c87f985da..49b59d60a 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -253,7 +253,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct } for (const Vec3d& p : island_new_points) { - m_output.emplace_back(float(p(0)), float(p(1)), structure.height, 0.4f, is_new_island); + m_output.emplace_back(float(p(0)), float(p(1)), structure.height, 0.2f, is_new_island); structure.supports_force += m_config.support_force; } } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 92078a8b9..2cf7f5f67 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3430,24 +3430,18 @@ void GLCanvas3D::Gizmos::set_sla_support_data(ModelObject* model_object, const G reinterpret_cast(it->second)->set_sla_support_data(model_object, selection); } -void GLCanvas3D::Gizmos::mouse_event(int action, const Vec2d& mouse_position, bool shift_down) + +// Returns true if the gizmo used the event to do something, false otherwise. +bool GLCanvas3D::Gizmos::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down) { if (!m_enabled) - return; + return false; GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->mouse_event(action, mouse_position, shift_down); -} + return reinterpret_cast(it->second)->mouse_event(action, mouse_position, shift_down); -void GLCanvas3D::Gizmos::delete_current_grabber(bool delete_all) -{ - if (!m_enabled) - return; - - GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); - if (it != m_gizmos.end()) - reinterpret_cast(it->second)->delete_current_point(delete_all); + return false; } void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selection) const @@ -5185,8 +5179,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'a': case 'A': case WXK_CONTROL_A: - if (m_gizmos.get_current_type() == Gizmos::SlaSupports) // Sla gizmo selects all support points - m_gizmos.mouse_event(5); + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::SelectAll)) // Sla gizmo selects all support points + m_dirty = true; else post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); break; @@ -5210,7 +5204,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #else /* __APPLE__ */ case WXK_DELETE: #endif /* __APPLE__ */ - post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break; + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::Delete)) + m_dirty = true; + else + post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); + break; + case '0': { select_view("iso"); break; } case '1': { select_view("top"); break; } case '2': { select_view("bottom"); break; } @@ -5252,11 +5251,9 @@ void GLCanvas3D::on_key_up(wxKeyEvent& evt) // see include/wx/defs.h enum wxKeyCode int keyCode = evt.GetKeyCode(); - // shift has been just released - SLA gizmo might want to close rectangular selection: - if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT) { - m_gizmos.mouse_event(2); + // shift has been just released - SLA gizmo might want to close rectangular selection. + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && keyCode == WXK_SHIFT && m_gizmos.mouse_event(SLAGizmoEventType::ShiftUp)) m_dirty = true; - } } void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) @@ -5399,25 +5396,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } -#if !ENABLE_IMGUI - else if ((m_gizmos.get_current_type() == Gizmos::SlaSupports) && gizmo_reset_rect_contains(*this, pos(0), pos(1))) - { - if (evt.LeftDown()) - { - m_gizmos.delete_current_grabber(true); - m_dirty = true; - } - } -#endif // not ENABLE_IMGUI else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse) { m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection); _update_gizmos_data(); m_dirty = true; } - else if (evt.LeftDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown()) // inform the gizmo about the event (rectangular selection init) + else if (evt.LeftDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown() && m_gizmos.mouse_event(SLAGizmoEventType::LeftDown, Vec2d(pos(0), pos(1)), evt.ShiftDown())) { - m_gizmos.mouse_event(1, Vec2d(pos(0), pos(1)), evt.ShiftDown()); + // the gizmo got the event and took some action, there is no need to do anything more } else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) { @@ -5434,10 +5421,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } - else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse() && evt.RightDown()) { + /*else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse() && evt.RightDown()) { if (m_gizmos.get_current_type() == Gizmos::SlaSupports) m_gizmos.delete_current_grabber(); - } + }*/ else if (view_toolbar_contains_mouse != -1) { if (m_view_toolbar != nullptr) @@ -5542,7 +5529,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } - else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1)) + else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) + && (m_mouse.drag.move_volume_idx != -1) && m_gizmos.get_current_type() != Gizmos::SlaSupports /* don't allow dragging objects with the Sla gizmo on */) { #if ENABLE_MOVE_MIN_THRESHOLD if (!m_mouse.drag.move_requires_threshold) @@ -5602,9 +5590,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } - else if (evt.Dragging() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown()) + else if (evt.Dragging() && m_gizmos.get_current_type() == Gizmos::SlaSupports && evt.ShiftDown() && m_gizmos.mouse_event(SLAGizmoEventType::Dragging, Vec2d(pos(0), pos(1)), evt.ShiftDown())) { - m_gizmos.mouse_event(3, Vec2d(pos(0), pos(1)), evt.ShiftDown()); + // the gizmo got the event and took some action, no need to do anything more here m_dirty = true; } else if (evt.Dragging() && !gizmos_overlay_contains_mouse) @@ -5662,6 +5650,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) _stop_timer(); m_layers_editing.accept_changes(*this); } + else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports && !m_gizmos.is_dragging() + && !m_mouse.dragging && m_gizmos.mouse_event(SLAGizmoEventType::LeftUp, Vec2d(pos(0), pos(1)), evt.ShiftDown())) + { + // the gizmo got the event and took some action, no need to do anything more + } else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { m_regenerate_volumes = false; @@ -5671,16 +5664,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // of the scene with the background processing data should be performed. post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); } - else if (evt.LeftUp() && m_gizmos.get_current_type() == Gizmos::SlaSupports/* && m_hover_volume_id != -1*/) + else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() + && !is_layers_editing_enabled() && m_gizmos.get_current_type() != Gizmos::SlaSupports) { - int id = m_selection.get_object_idx(); + // SLA gizmo cannot be deselected by clicking in canvas area to avoid inadvertent unselection and losing manual changes - if ((id != -1) && (m_model != nullptr)) { - m_gizmos.mouse_event(2, Vec2d(pos(0), pos(1)), evt.ShiftDown()); - } - } - else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) - { // deselect and propagate event through callback if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) { @@ -5713,10 +5701,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) do_rotate(); break; } - case Gizmos::SlaSupports: - // End of mouse dragging, update the SLAPrint/SLAPrintObjects with the new support points. - post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - break; default: break; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index db630988c..ff69f1ef8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -132,6 +132,17 @@ wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +// this describes events being passed from GLCanvas3D to SlaSupport gizmo +enum class SLAGizmoEventType { + LeftDown = 1, + LeftUp, + Dragging, + Delete, + SelectAll, + ShiftUp +}; + + class GLCanvas3D { struct GCodePreviewVolumeIndex @@ -789,7 +800,7 @@ private: void set_flattening_data(const ModelObject* model_object); void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); - void mouse_event(int action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false); + bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false); void delete_current_grabber(bool delete_all = false); void render_current_gizmo(const Selection& selection) const; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index e58eb8500..6f387ceb3 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1795,8 +1795,9 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G if (po->model_object()->id() == model_object->id()) { const Eigen::MatrixXd& points = po->get_support_points(); for (unsigned int i=0; itrafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), - points(i, 3), points(i, 4)); + m_editing_mode_cache.push_back(std::make_pair(sla::SupportPoint(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), + points(i, 3), points(i, 4)), + false)); break; } } @@ -1895,7 +1896,8 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b float render_color[3]; for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i) { - const Vec3f& point_pos = m_editing_mode_cache[i].pos; + const Vec3f& point_pos = m_editing_mode_cache[i].first.pos; + const bool point_selected = m_editing_mode_cache[i].second; // first precalculate the grabber position in world coordinates, so that the grabber // is not scaled with the object (as it would be if rendered with current gl matrix). Eigen::Matrix glmatrix; @@ -1919,11 +1921,11 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b render_color[2] = picking_color_component(i); } else { // normal rendering - bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].is_new_island; + bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].first.is_new_island; if (m_editing_mode) { - render_color[0] = supports_new_island ? 0.f : 1.f; - render_color[1] = 0.f; - render_color[2] = supports_new_island ? 1.f : 0.f; + render_color[0] = point_selected ? 0.f : (supports_new_island ? 0.f : 1.f); + render_color[1] = point_selected ? 1.f : 0.f; + render_color[2] = point_selected ? 0.f : (supports_new_island ? 1.f : 0.f); } else for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f; @@ -1934,7 +1936,7 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b ::glPushMatrix(); ::glLoadIdentity(); ::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift); - ::gluSphere(m_quadric, m_editing_mode_cache[i].head_front_radius, 64, 36); + ::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius, 64, 36); ::glPopMatrix(); } @@ -1982,7 +1984,9 @@ void GLGizmoSlaSupports::update_mesh() m_AABB.init(m_V, m_F); // we'll now reload support points (selection might have changed): - m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode_cache.clear(); + for (const sla::SupportPoint& point : m_model_object->sla_support_points) + m_editing_mode_cache.push_back(std::make_pair(point, false)); } Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) @@ -2025,48 +2029,74 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) return 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)); } -void GLGizmoSlaSupports::mouse_event(int action, const Vec2d& mouse_position, bool shift_down) +// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. +// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is +// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo +// concludes that the event was not intended for it, it should return false. +bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down) { if (!m_editing_mode) - return; + return false; // left down - show the selection rectangle: - if (action == 1 && shift_down) { - m_selection_rectangle_active = true; - m_selection_rectangle_start_corner = mouse_position; - m_selection_rectangle_end_corner = mouse_position; - m_canvas_width = m_parent.get_canvas_size().get_width(); - m_canvas_height = m_parent.get_canvas_size().get_height(); + if (action == SLAGizmoEventType::LeftDown && shift_down) { + if (m_hover_id == -1) { + m_selection_rectangle_active = true; + m_selection_rectangle_start_corner = mouse_position; + m_selection_rectangle_end_corner = mouse_position; + m_canvas_width = m_parent.get_canvas_size().get_width(); + m_canvas_height = m_parent.get_canvas_size().get_height(); + } + else + m_editing_mode_cache[m_hover_id].second = true; + + return true; } // dragging the selection rectangle: - if (action == 3 && m_selection_rectangle_active) + if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) { m_selection_rectangle_end_corner = mouse_position; + return true; + } // mouse up without selection rectangle - place point on the mesh: - if (action == 2 && !m_selection_rectangle_active) { + if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) { + if (m_ignore_up_event) { + m_ignore_up_event = false; + return false; + } + int instance_id = m_parent.get_selection().get_instance_idx(); if (m_old_instance_id != instance_id) { bool something_selected = (m_old_instance_id != -1); m_old_instance_id = instance_id; if (something_selected) - return; + return false; } if (instance_id == -1) - return; + return false; + + // Regardless of whether the user clicked the object or not, we will unselect all points: + for (unsigned int i=0; iinstances[m_active_instance]->get_transformation().get_matrix(); GLint viewport[4]; ::glGetIntegerv(GL_VIEWPORT, viewport); @@ -2082,68 +2112,61 @@ void GLGizmoSlaSupports::mouse_event(int action, const Vec2d& mouse_position, bo // bounding box created from the rectangle corners - will take care of order of the corners BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); - for (sla::SupportPoint& support_point : m_editing_mode_cache) { + for (std::pair& point_and_selection : m_editing_mode_cache) { + const sla::SupportPoint& support_point = point_and_selection.first; Vec3f pos = instance_matrix.cast() * support_point.pos; pos(2) += z_offset; GLdouble out_x, out_y, out_z; ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); out_y = m_canvas_height - out_y; - /*GLfloat depth_comp; - ::glReadPixels( out_x, out_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth_comp); - if (depth_comp > out_z + EPSILON) // the point is obscured by something else - continue;*/ - if (rectangle.contains(Point(out_x, out_y))) - support_point.head_front_radius = 1.f; + point_and_selection.second = true; } m_selection_rectangle_active = false; + return true; } - if (action == 4) { - // shift has been released + if (action == SLAGizmoEventType::Delete) { + // delete key pressed + delete_selected_points(); + return true; } - if (action == 5) { - // ctrl+A : select all - std::cout << "select all..." << std::endl; + if (action == SLAGizmoEventType::SelectAll) { + for (auto& point_and_selection : m_editing_mode_cache) + point_and_selection.second = true; + return true; } + + return false; } -void GLGizmoSlaSupports::delete_current_point(bool delete_all) +void GLGizmoSlaSupports::delete_selected_points() { - if (!m_editing_mode && !delete_all) + if (!m_editing_mode) return; - if (delete_all) { - m_editing_mode_cache.clear(); - - // This should trigger the support generation - // wxGetApp().plater()->reslice(); + for (unsigned int idx=0; idxreslice(); } - else - if (m_hover_id != -1) { - if (!m_editing_mode_cache[m_hover_id].is_new_island || !m_lock_unique_islands) { - m_editing_mode_cache.erase(m_editing_mode_cache.begin() + m_hover_id); - m_hover_id = -1; - // 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_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].is_new_island || !m_lock_unique_islands)) { + if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].first.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_editing_mode_cache[m_hover_id].pos = new_pos; - m_editing_mode_cache[m_hover_id].is_new_island = false; + m_editing_mode_cache[m_hover_id].first.pos = new_pos; + m_editing_mode_cache[m_hover_id].first.is_new_island = false; // Do not update immediately, wait until the mouse is released. // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -2193,29 +2216,13 @@ RENDER_AGAIN: ImGui::PushItemWidth(100.0f); - /*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; - - if (force_refresh) { // mode has just changed! - if (m_editing_mode) - m_editing_mode_cache = m_model_object->sla_support_points; - else - m_model_object->sla_support_points = m_editing_mode_cache; - } - */ - bool force_refresh = false; - bool remove_all_points = false; + bool remove_selected = false; bool old_editing_state = m_editing_mode; 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(_(L("Shift + Left (+ drag) - select point(s)"))); m_imgui->text(" "); // vertical gap std::vector options = {"0.2", "0.4", "0.6", "0.8", "1.0"}; @@ -2223,28 +2230,38 @@ RENDER_AGAIN: 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); + float current_number = atof(str); + if (std::abs(current_number - m_new_point_head_diameter) > 0.001) { + force_refresh = true; + m_new_point_head_diameter = current_number; + for (auto& point_and_selection : m_editing_mode_cache) + if (point_and_selection.second) + point_and_selection.first.head_front_radius = current_number / 2.f; + } 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; - remove_all_points = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_editing_mode_cache.size())+")")); + remove_selected = m_imgui->button(_(L("Remove selected points"))); m_imgui->text(" "); // vertical gap bool apply_changes = m_imgui->button(_(L("Apply changes"))); if (apply_changes) { - m_model_object->sla_support_points = m_editing_mode_cache; + m_model_object->sla_support_points.clear(); + for (const std::pair& point_and_selection : m_editing_mode_cache) + m_model_object->sla_support_points.push_back(point_and_selection.first); m_editing_mode = false; force_refresh = true; m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } ImGui::SameLine(); - bool discard_changes = m_imgui->button(_(L("Cancel"))); + bool discard_changes = m_imgui->button(_(L("Discard changes"))); if (discard_changes) { - m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode_cache.clear(); + for (const sla::SupportPoint& point : m_model_object->sla_support_points) + m_editing_mode_cache.push_back(std::make_pair(point, false)); m_editing_mode = false; force_refresh = true; } @@ -2258,16 +2275,45 @@ RENDER_AGAIN: m_imgui->text(""); bool generate =m_imgui->button(_(L("Auto-generate points"))); - force_refresh |= generate; + if (generate) { - m_model_object->sla_support_points.clear(); - m_editing_mode_cache.clear(); - wxGetApp().plater()->reslice(); + ImGui::OpenPopup(_(L("Warning"))); + m_show_modal = true; + force_refresh = true; } + + if (m_show_modal) { + if (ImGui::BeginPopupModal(_(L("Warning")), &m_show_modal/*, ImGuiWindowFlags_NoDecoration*/)) + { + m_imgui->text(_(L("This will erase all your manual changes."))); + + if (m_imgui->button(_(L("Continue")))) + { + ImGui::CloseCurrentPopup(); + m_show_modal = false; + + m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); + wxGetApp().plater()->reslice(); + } + ImGui::SameLine(); + if (m_imgui->button(_(L("Cancel")))) { + ImGui::CloseCurrentPopup(); + m_show_modal = false; + } + ImGui::EndPopup(); + } + + if (!m_show_modal) + force_refresh = true; + } + ImGui::SameLine(); bool editing_clicked = m_imgui->button("Editing"); if (editing_clicked) { - m_editing_mode_cache = m_model_object->sla_support_points; + m_editing_mode_cache.clear(); + for (const sla::SupportPoint& point : m_model_object->sla_support_points) + m_editing_mode_cache.push_back(std::make_pair(point, false)); m_editing_mode = true; } } @@ -2280,10 +2326,10 @@ RENDER_AGAIN: } - if (remove_all_points) { + if (remove_selected) { force_refresh = false; m_parent.reload_scene(true); - delete_current_point(true); + delete_selected_points(); if (first_run) { first_run = false; goto RENDER_AGAIN; @@ -2324,9 +2370,20 @@ void GLGizmoSlaSupports::on_set_state() if (m_state == Off) { m_parent.toggle_model_objects_visibility(true); m_editing_mode = false; + if (m_show_modal) { + m_show_modal = false; + on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly + } } } +void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection) +{ + if (m_hover_id != -1) + for (unsigned int i=0;i m_editing_mode_cache; + std::vector> m_editing_mode_cache; // a support point and whether it is currently selected bool m_selection_rectangle_active = false; Vec2d m_selection_rectangle_start_corner; Vec2d m_selection_rectangle_end_corner; + bool m_ignore_up_event = false; + bool m_show_modal = false; int m_canvas_width; int m_canvas_height; protected: void on_set_state() override; + void on_start_dragging(const GLCanvas3D::Selection& selection) override; #if ENABLE_IMGUI virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) override; From d1529296c6e9ae56b4fb12a69c346416b0ec07c1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 11 Feb 2019 17:06:39 +0100 Subject: [PATCH 15/28] Added a wxMessageDialog as an alternative to the ImGui modal, in case it turns out not good enough --- src/slic3r/GUI/GLGizmo.cpp | 24 ++++++++++++++++++++---- src/slic3r/GUI/GLGizmo.hpp | 5 +++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 6f387ceb3..85551d23c 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2274,18 +2274,32 @@ RENDER_AGAIN: m_imgui->text(""); - bool generate =m_imgui->button(_(L("Auto-generate points"))); + bool generate = m_imgui->button(_(L("Auto-generate points"))); if (generate) { +#if SLAGIZMO_IMGUI_MODAL ImGui::OpenPopup(_(L("Warning"))); m_show_modal = true; force_refresh = true; +#else + wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( + "Autogeneration will erase all currently assigned points.\n\n" + "Are you sure you want to do it?\n" + )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) { + m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); + wxGetApp().plater()->reslice(); + } +#endif } - +#if SLAGIZMO_IMGUI_MODAL if (m_show_modal) { if (ImGui::BeginPopupModal(_(L("Warning")), &m_show_modal/*, ImGuiWindowFlags_NoDecoration*/)) { - m_imgui->text(_(L("This will erase all your manual changes."))); + m_imgui->text(_(L("Autogeneration will erase all currently assigned points."))); + m_imgui->text(""); + m_imgui->text(_(L("Are you sure you want to do it?"))); if (m_imgui->button(_(L("Continue")))) { @@ -2307,7 +2321,7 @@ RENDER_AGAIN: if (!m_show_modal) force_refresh = true; } - +#endif ImGui::SameLine(); bool editing_clicked = m_imgui->button("Editing"); if (editing_clicked) { @@ -2370,10 +2384,12 @@ void GLGizmoSlaSupports::on_set_state() if (m_state == Off) { m_parent.toggle_model_objects_visibility(true); m_editing_mode = false; +#if SLAGIZMO_IMGUI_MODAL if (m_show_modal) { m_show_modal = false; on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly } +#endif } } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 15c3779f5..b80916db7 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -437,6 +437,9 @@ protected: } }; + + +#define SLAGIZMO_IMGUI_MODAL 1 class GLGizmoSlaSupports : public GLGizmoBase { private: @@ -493,7 +496,9 @@ private: Vec2d m_selection_rectangle_start_corner; Vec2d m_selection_rectangle_end_corner; bool m_ignore_up_event = false; +#if SLAGIZMO_IMGUI_MODAL bool m_show_modal = false; +#endif int m_canvas_width; int m_canvas_height; From fcc1b2ad69ff00a455b4c3637e6a558d35bbc68f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 17 Feb 2019 13:05:22 +0100 Subject: [PATCH 16/28] Reworked polygon sampling for SLA auto support generation. Conditional compilation of an igl winding number tree for SLA support generator, as it is not used as of now and initialization of the tree is expensive. Fixed issue with passing the new SLA point definition to the back end and back to the UI. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 262 ++++++++++++++++++------ src/libslic3r/SLA/SLACommon.hpp | 4 + src/libslic3r/SLA/SLASupportTree.cpp | 4 +- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 6 + src/libslic3r/SLAPrint.cpp | 15 +- src/libslic3r/SLAPrint.hpp | 2 +- src/slic3r/GUI/GLGizmo.cpp | 8 +- 7 files changed, 225 insertions(+), 76 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index c87f985da..4389fd71f 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -9,6 +9,8 @@ #include "SVG.hpp" #include "Point.hpp" #include "ClipperUtils.hpp" +#include "Tesselate.hpp" +#include "libslic3r.h" #include #include @@ -121,15 +123,15 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: Structure& top = structures_new.back(); //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. // At least it is now using a bounding box check for pre-filtering. - for (Structure& bottom : structures_old) - if (top.overlaps(bottom)) { - top.structures_below.push_back(&bottom); + for (Structure& bottom : structures_old) + if (top.overlaps(bottom)) { + top.structures_below.push_back(&bottom); float centroids_dist = (bottom.centroid - top.centroid).norm(); // Penalization resulting from centroid offset: -// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); +// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, 80.f * centroids_dist * centroids_dist / bottom.area)); // Penalization resulting from increasing polygon area: - bottom.supports_force *= std::min(1.f, 20.f * bottom.area / top.area); + bottom.supports_force *= std::min(1.f, 20.f * bottom.area / top.area); } } @@ -154,7 +156,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: else // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. - for (const ExPolygon& p : diff_ex(to_polygons(*s.polygon), offset(s.expolygons_below(), between_layers_offset))) + for (const ExPolygon& p : diff_ex(to_polygons(*s.polygon), offset(s.expolygons_below(), between_layers_offset))) //FIXME is it an island point or not? Vojtech thinks it is. uniformly_cover(p, s); } @@ -163,12 +165,12 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: for (Structure& s : structures_new) { // Areas not supported by the areas below. ExPolygons e = diff_ex(to_polygons(*s.polygon), s.polygons_below()); - float e_area = 0.f; - for (const ExPolygon &ex : e) - e_area += float(ex.area()); + float e_area = 0.f; + for (const ExPolygon &ex : e) + e_area += float(ex.area()); // Penalization resulting from large diff from the last layer: // s.supports_force /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); - s.supports_force /= std::max(1.f, 0.17f * (e_area * float(SCALING_FACTOR * SCALING_FACTOR)) / s.area); + s.supports_force /= std::max(1.f, 0.17f * (e_area * float(SCALING_FACTOR * SCALING_FACTOR)) / s.area); if (s.area * m_config.tear_pressure > s.supports_force) { //FIXME Don't calculate area inside the compare function! @@ -196,64 +198,204 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: } } +std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng) +{ + // Triangulate the polygon with holes into triplets of 3D points. + std::vector triangles = Slic3r::triangulate_expolygon_2f(expoly); + + std::vector out; + if (! triangles.empty()) + { + // Calculate area of each triangle. + std::vector areas; + areas.reserve(triangles.size() / 3); + for (size_t i = 0; i < triangles.size(); ) { + const Vec2f &a = triangles[i ++]; + const Vec2f v1 = triangles[i ++] - a; + const Vec2f v2 = triangles[i ++] - a; + areas.emplace_back(0.5f * std::abs(cross2(v1, v2))); + if (i != 3) + // Prefix sum of the areas. + areas.back() += areas[areas.size() - 2]; + } + + size_t num_samples = size_t(ceil(areas.back() * samples_per_mm2)); + std::uniform_real_distribution<> random_triangle(0., double(areas.back())); + std::uniform_real_distribution<> random_float(0., 1.); + for (size_t i = 0; i < num_samples; ++ i) { + double r = random_triangle(rng); + size_t idx_triangle = std::min(std::upper_bound(areas.begin(), areas.end(), (float)r) - areas.begin(), areas.size() - 1) * 3; + // Select a random point on the triangle. + double u = float(sqrt(random_float(rng))); + double v = float(random_float(rng)); + const Vec2f &a = triangles[idx_triangle ++]; + const Vec2f &b = triangles[idx_triangle++]; + const Vec2f &c = triangles[idx_triangle]; + const Vec2f x = a * (1.f - u) + b * (u * (1.f - v)) + c * (v * u); + out.emplace_back(x); + } + } + return out; +} + +std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) +{ + std::vector out = sample_expolygon(expoly, samples_per_mm2, rng); + double point_stepping_scaled = scale_(1.f) / samples_per_mm_boundary; + for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) { + const Polygon &contour = (i_contour == 0) ? expoly.contour : expoly.holes[i_contour - 1]; + const Points pts = contour.equally_spaced_points(point_stepping_scaled); + for (size_t i = 0; i < pts.size(); ++ i) + out.emplace_back(unscale(pts[i].x()), unscale(pts[i].y())); + } + return out; +} + +std::vector poisson_disk_from_samples(const std::vector &raw_samples, float radius) +{ + Vec2f corner_min(FLT_MAX, FLT_MAX); + for (const Vec2f &pt : raw_samples) { + corner_min.x() = std::min(corner_min.x(), pt.x()); + corner_min.y() = std::min(corner_min.y(), pt.y()); + } + + // Assign the raw samples to grid cells, sort the grid cells lexicographically. + struct RawSample { + Vec2f coord; + Vec2i cell_id; + }; + std::vector raw_samples_sorted; + RawSample sample; + for (const Vec2f &pt : raw_samples) { + sample.coord = pt; + sample.cell_id = ((pt - corner_min) / radius).cast(); + raw_samples_sorted.emplace_back(sample); + } + std::sort(raw_samples_sorted.begin(), raw_samples_sorted.end(), [](const RawSample &lhs, const RawSample &rhs) + { return lhs.cell_id.x() < rhs.cell_id.x() || (lhs.cell_id.x() == rhs.cell_id.x() && lhs.cell_id.y() < rhs.cell_id.y()); }); + + struct PoissonDiskGridEntry { + // Resulting output sample points for this cell: + enum { + max_positions = 4 + }; + Vec2f poisson_samples[max_positions]; + int num_poisson_samples = 0; + + // Index into raw_samples: + int first_sample_idx; + int sample_cnt; + }; + + struct CellIDHash { + std::size_t operator()(const Vec2i &cell_id) { + return std::hash()(cell_id.x()) ^ std::hash()(cell_id.y() * 593); + } + }; + + // Map from cell IDs to hash_data. Each hash_data points to the range in raw_samples corresponding to that cell. + // (We could just store the samples in hash_data. This implementation is an artifact of the reference paper, which + // is optimizing for GPU acceleration that we haven't implemented currently.) + typedef std::unordered_map Cells; + std::unordered_map cells; + { + Cells::iterator last_cell_id_it; + Vec2i last_cell_id(-1, -1); + for (int i = 0; i < raw_samples_sorted.size(); ++ i) { + const RawSample &sample = raw_samples_sorted[i]; + if (sample.cell_id == last_cell_id) { + // This sample is in the same cell as the previous, so just increase the count. Cells are + // always contiguous, since we've sorted raw_samples_sorted by cell ID. + ++ last_cell_id_it->second.sample_cnt; + } else { + // This is a new cell. + PoissonDiskGridEntry data; + data.first_sample_idx = i; + data.sample_cnt = 1; + auto result = cells.insert({sample.cell_id, data}); + last_cell_id = sample.cell_id; + last_cell_id_it = result.first; + } + } + } + + const int max_trials = 5; + const float radius_squared = radius * radius; + for (int trial = 0; trial < max_trials; ++ trial) { + // Create sample points for each entry in cells. + for (auto &it : cells) { + const Vec2i &cell_id = it.first; + PoissonDiskGridEntry &cell_data = it.second; + // This cell's raw sample points start at first_sample_idx. On trial 0, try the first one. On trial 1, try first_sample_idx + 1. + int next_sample_idx = cell_data.first_sample_idx + trial; + if (trial >= cell_data.sample_cnt) + // There are no more points to try for this cell. + continue; + const RawSample &candidate = raw_samples_sorted[next_sample_idx]; + // See if this point conflicts with any other points in this cell, or with any points in + // neighboring cells. Note that it's possible to have more than one point in the same cell. + bool conflict = false; + for (int i = -1; i < 2 && ! conflict; ++ i) { + for (int j = -1; j < 2; ++ j) { + const auto &it_neighbor = cells.find(cell_id + Vec2i(i, j)); + if (it_neighbor != cells.end()) { + const PoissonDiskGridEntry &neighbor = it_neighbor->second; + for (int i_sample = 0; i_sample < neighbor.num_poisson_samples; ++ i_sample) + if ((neighbor.poisson_samples[i_sample] - candidate.coord).squaredNorm() < radius_squared) { + conflict = true; + break; + } + } + } + } + if (! conflict) { + // Store the new sample. + assert(cell_data.num_poisson_samples < cell_data.max_positions); + if (cell_data.num_poisson_samples < cell_data.max_positions) + cell_data.poisson_samples[cell_data.num_poisson_samples ++] = candidate.coord; + } + } + } + + // Copy the results to the output. + std::vector out; + for (const auto it : cells) + for (int i = 0; i < it.second.num_poisson_samples; ++ i) + out.emplace_back(it.second.poisson_samples[i]); + return out; +} + 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; + //FIXME why? + const float poisson_radius = 1.f / (5.f * density_horizontal); +// const float poisson_radius = 1.f / (15.f * density_horizontal); + const float samples_per_mm2 = 30.f / (float(M_PI) * poisson_radius * poisson_radius); - // We will cover the island another way. - // For now we'll just place the points randomly not too close to the others. //FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon. - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> dis(0., 1.); + std::random_device rd; + std::mt19937 rng(rd()); + std::vector raw_samples = sample_expolygon_with_boundary(island, samples_per_mm2, 5.f / poisson_radius, rng); + std::vector poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius); - std::vector island_new_points; - const BoundingBox bb = get_extents(island); - const int refused_limit = (int)floor(30.f * ((float)bb.size()(0)*bb.size()(1) / (float)island.area()) + 0.5f); - int refused_points = 0; - //FIXME this is very inefficient (may be blind) for long narrow polygons (thin crescent, thin ring). Use some search structure: Triangulate the polygon first? - //FIXME use a low discrepancy sequence, Poisson sampling. - // Poisson sampling (adapt from 3D to 2D by triangulation): https://github.com/zewt/maya-implicit-skinning/blob/master/src/meshes/vcg_lib/utils_sampling.cpp - 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)); +#ifdef SLA_AUTOSUPPORTS_DEBUG + { + static int irun = 0; + Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(island)); + svg.draw(island); + for (const Vec2f &pt : raw_samples) + svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red"); + for (const Vec2f &pt : poisson_samples) + svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "blue"); + } +#endif /* NDEBUG */ - Vec3d unscaled_out = unscale(out(0), out(1), 0); - 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; - break; - } - - 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) { - m_output.emplace_back(float(p(0)), float(p(1)), structure.height, 0.4f, is_new_island); + assert(! poisson_samples.empty()); + for (const Vec2f &pt : poisson_samples) { + m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.4f, is_new_island); structure.supports_force += m_config.support_force; } } diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index dcf991aec..ab535fb9e 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -3,6 +3,7 @@ #include +// #define SLIC3R_SLA_NEEDS_WINDTREE namespace Slic3r { @@ -11,6 +12,7 @@ typedef Eigen::Matrix Vec3f; typedef Eigen::Matrix Vec3d; class TriangleMesh; + namespace sla { struct SupportPoint { @@ -115,11 +117,13 @@ public: int F_idx() const { return m_fidx; } }; +#ifdef SLIC3R_SLA_NEEDS_WINDTREE // The signed distance from a point to the mesh. Outputs the distance, // the index of the triangle and the closest point in mesh coordinate space. si_result signed_distance(const Vec3d& p) const; bool inside(const Vec3d& p) const; +#endif /* SLIC3R_SLA_NEEDS_WINDTREE */ }; diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 87c967f8b..3e36dfd85 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -552,14 +552,12 @@ enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers PointSet to_point_set(const std::vector &v) { - PointSet ret(v.size(), 5); + PointSet ret(v.size(), 3); 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/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index d3af1eac8..6a3b71e7d 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -95,7 +95,9 @@ size_t SpatIndex::size() const class EigenMesh3D::AABBImpl: public igl::AABB { public: +#ifdef SLIC3R_SLA_NEEDS_WINDTREE igl::WindingNumberAABB windtree; +#endif /* SLIC3R_SLA_NEEDS_WINDTREE */ }; EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { @@ -136,7 +138,9 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { // Build the AABB accelaration tree m_aabb->init(m_V, m_F); +#ifdef SLIC3R_SLA_NEEDS_WINDTREE m_aabb->windtree.set_mesh(m_V, m_F); +#endif /* SLIC3R_SLA_NEEDS_WINDTREE */ } EigenMesh3D::~EigenMesh3D() {} @@ -168,6 +172,7 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return ret; } +#ifdef SLIC3R_SLA_NEEDS_WINDTREE EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { double sign = 0; double sqdst = 0; int i = 0; Vec3d c; igl::signed_distance_winding_number(*m_aabb, m_V, m_F, m_aabb->windtree, @@ -179,6 +184,7 @@ EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { bool EigenMesh3D::inside(const Vec3d &p) const { return m_aabb->windtree.inside(p); } +#endif /* SLIC3R_SLA_NEEDS_WINDTREE */ /* **************************************************************************** * Misc functions diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index e00437862..4865150c2 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -25,7 +25,7 @@ using SupportTreePtr = std::unique_ptr; class SLAPrintObject::SupportData { public: sla::EigenMesh3D emesh; // index-triangle representation - sla::PointSet support_points; // all the support points (manual/auto) + std::vector support_points; // all the support points (manual/auto) SupportTreePtr support_tree_ptr; // the supports SlicedSupports support_slices; // sliced supports std::vector level_ids; @@ -548,18 +548,17 @@ void SLAPrint::process() // Now let's extract the result. const std::vector& points = auto_supports.output(); this->throw_if_canceled(); - po.m_supportdata->support_points = sla::to_point_set(points); + po.m_supportdata->support_points = points; BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " - << po.m_supportdata->support_points.rows(); + << po.m_supportdata->support_points.size(); // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); } else { // There are some points on the front-end, no calculation will be done. - po.m_supportdata->support_points = - sla::to_point_set(po.transformed_support_points()); + po.m_supportdata->support_points = po.transformed_support_points(); } }; @@ -597,7 +596,7 @@ void SLAPrint::process() ctl.cancelfn = [this]() { throw_if_canceled(); }; po.m_supportdata->support_tree_ptr.reset( - new SLASupportTree(po.m_supportdata->support_points, + new SLASupportTree(sla::to_point_set(po.m_supportdata->support_points), po.m_supportdata->emesh, scfg, ctl)); // Create the unified mesh @@ -608,7 +607,7 @@ void SLAPrint::process() po.m_supportdata->support_tree_ptr->merged_mesh(); BOOST_LOG_TRIVIAL(debug) << "Processed support point count " - << po.m_supportdata->support_points.rows(); + << po.m_supportdata->support_points.size(); // Check the mesh for later troubleshooting. if(po.support_mesh().empty()) @@ -1168,7 +1167,7 @@ const std::vector EMPTY_SLICES; const TriangleMesh EMPTY_MESH; } -const Eigen::MatrixXd& SLAPrintObject::get_support_points() const +const std::vector& SLAPrintObject::get_support_points() const { return m_supportdata->support_points; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index fc4e9a4d4..9aaba6a47 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -91,7 +91,7 @@ public: const std::vector& get_support_slices() const; // This method returns the support points of this SLAPrintObject. - const Eigen::MatrixXd& get_support_points() const; + const std::vector& get_support_points() const; // An index record referencing the slices // (get_model_slices(), get_support_slices()) where the keys are the height diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index f9191b69b..eecf1de2c 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1793,10 +1793,10 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G if (m_editing_mode_cache.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) { 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; itrafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)), - points(i, 3), points(i, 4)); + const std::vector& points = po->get_support_points(); + auto mat = po->trafo().inverse().cast(); + for (unsigned int i=0; i Date: Sun, 17 Feb 2019 13:26:17 +0100 Subject: [PATCH 17/28] Fix of a merge error. --- src/slic3r/GUI/GLGizmo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 826258714..1d66d8e79 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1796,7 +1796,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G const std::vector& points = po->get_support_points(); auto mat = po->trafo().inverse().cast(); for (unsigned int i=0; i Date: Mon, 18 Feb 2019 11:46:06 +0100 Subject: [PATCH 18/28] Parallelization of large part of the SLA support point calculation. New 3D grid to check, whether two support points are not too close. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 204 +++++++++++++++++--------- src/libslic3r/SLA/SLAAutoSupports.hpp | 100 +++++++++++-- 2 files changed, 220 insertions(+), 84 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 38add28ee..7321387af 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -93,99 +93,152 @@ void SLAAutoSupports::project_onto_mesh(std::vector& points) }); } +static std::vector make_layers( + const std::vector& slices, const std::vector& heights, + std::function throw_on_cancel) +{ + assert(slices.size() == heights.size()); + + // Allocate empty layers. + std::vector layers; + layers.reserve(slices.size()); + for (size_t i = 0; i < slices.size(); ++ i) + layers.emplace_back(i, heights[i]); + + // FIXME: calculate actual pixel area from printer config: + //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // + const float pixel_area = pow(0.047f, 2.f); + + // Use a reasonable granularity to account for the worker thread synchronization cost. + tbb::parallel_for(tbb::blocked_range(0, layers.size(), 32), + [&layers, &slices, &heights, pixel_area, throw_on_cancel](const tbb::blocked_range& range) { + for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { + if ((layer_id % 8) == 0) + // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. + throw_on_cancel(); + SLAAutoSupports::MyLayer &layer = layers[layer_id]; + const ExPolygons &islands = slices[layer_id]; + //FIXME WTF? + const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0])); + layer.islands.reserve(islands.size()); + for (const ExPolygon &island : islands) { + float area = float(island.area() * SCALING_FACTOR * SCALING_FACTOR); + if (area >= pixel_area) + //FIXME this is not a correct centroid of a polygon with holes. + layer.islands.emplace_back(layer, island, get_extents(island.contour), Slic3r::unscale(island.contour.centroid()).cast(), area, height); + } + } + }); + + // Calculate overlap of successive layers. Link overlapping islands. + tbb::parallel_for(tbb::blocked_range(1, layers.size(), 8), + [&layers, &heights, throw_on_cancel](const tbb::blocked_range& range) { + for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { + if ((layer_id % 2) == 0) + // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. + throw_on_cancel(); + SLAAutoSupports::MyLayer &layer_above = layers[layer_id]; + SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1]; + //FIXME WTF? + const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0])); + const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); + const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports + const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); + //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. + for (SLAAutoSupports::Structure &top : layer_above.islands) { + for (SLAAutoSupports::Structure &bottom : layer_below.islands) + if (top.overlaps(bottom)) { + top.islands_below.emplace_back(&bottom); + bottom.islands_above.emplace_back(&top); + } + if (! top.islands_below.empty()) { + Polygons top_polygons = to_polygons(*top.polygon); + Polygons bottom_polygons = top.polygons_below(); + top.overhangs = diff_ex(top_polygons, bottom_polygons); + if (! top.overhangs.empty()) { + top.overhangs_area = 0.f; + std::vector> expolys_with_areas; + for (ExPolygon &ex : top.overhangs) { + float area = float(ex.area()); + expolys_with_areas.emplace_back(&ex, area); + top.overhangs_area += area; + } + std::sort(expolys_with_areas.begin(), expolys_with_areas.end(), + [](const std::pair &p1, const std::pair &p2) + { return p1.second > p2.second; }); + ExPolygons overhangs_sorted; + for (auto &p : expolys_with_areas) + overhangs_sorted.emplace_back(std::move(*p.first)); + top.overhangs = std::move(overhangs_sorted); + top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR); + top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset)); + } + } + } + } + }); + + return layers; +} + void SLAAutoSupports::process(const std::vector& slices, const std::vector& heights) { +#ifdef SLA_AUTOSUPPORTS_DEBUG std::vector> islands; - std::vector structures_old; - std::vector structures_new; +#endif /* SLA_AUTOSUPPORTS_DEBUG */ - for (unsigned int i = 0; i layers = make_layers(slices, heights, m_throw_on_cancel); - //FIXME WTF? - const float height = (i>2 ? heights[i-3] : heights[0]-(heights[1]-heights[0])); - const float layer_height = (i!=0 ? heights[i]-heights[i-1] : heights[0]); - - const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports - const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); + PointGrid3D point_grid; + point_grid.cell_size = Vec3f(10.f, 10.f, 10.f); - // FIXME: calculate actual pixel area from printer config: - //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option("display_width") / wxGetApp().preset_bundle->project_config.option("display_pixels_x"), 2.f); // - const float pixel_area = pow(0.047f, 2.f); - - // Check all ExPolygons on this slice and check whether they are new or belonging to something below. - for (const ExPolygon& polygon : expolys_top) { - float area = float(polygon.area() * SCALING_FACTOR * SCALING_FACTOR); - if (area < pixel_area) - continue; - //FIXME this is not a correct centroid of a polygon with holes. - structures_new.emplace_back(polygon, get_extents(polygon.contour), Slic3r::unscale(polygon.contour.centroid()).cast(), area, height); - Structure& top = structures_new.back(); - //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. - // At least it is now using a bounding box check for pre-filtering. - for (Structure& bottom : structures_old) - if (top.overlaps(bottom)) { - top.structures_below.push_back(&bottom); - float centroids_dist = (bottom.centroid - top.centroid).norm(); - // Penalization resulting from centroid offset: + for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) { + SLAAutoSupports::MyLayer &layer_top = layers[layer_id]; + for (Structure &top : layer_top.islands) + for (Structure *bottom : top.islands_below) { + float centroids_dist = (bottom->centroid - top.centroid).norm(); + // Penalization resulting from centroid offset: // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); - bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, 80.f * centroids_dist * centroids_dist / bottom.area)); - // Penalization resulting from increasing polygon area: - bottom.supports_force *= std::min(1.f, 20.f * bottom.area / top.area); - } - } - + bottom->supports_force *= std::min(1.f, 1.f - std::min(1.f, 80.f * centroids_dist * centroids_dist / bottom->area)); + // Penalization resulting from increasing polygon area: + bottom->supports_force *= std::min(1.f, 20.f * bottom->area / top.area); + } // Let's assign proper support force to each of them: - for (const Structure& below : structures_old) { - std::vector above_list; - float above_area = 0.f; - for (Structure& new_str : structures_new) - for (const Structure* below1 : new_str.structures_below) - if (&below == below1) { - above_list.push_back(&new_str); - above_area += above_list.back()->area; - } - for (Structure* above : above_list) - above->supports_force += below.supports_force * above->area / above_area; + if (layer_id > 0) { + for (Structure &below : layers[layer_id - 1].islands) { + float above_area = 0.f; + for (Structure *above : below.islands_above) + above_area += above->area; + for (Structure *above : below.islands_above) + above->supports_force += below.supports_force * above->area / above_area; + } } - // Now iterate over all polygons and append new points if needed. - for (Structure& s : structures_new) { - if (s.structures_below.empty()) // completely new island - needs support no doubt - uniformly_cover(*s.polygon, s, true); + for (Structure &s : layer_top.islands) { + if (s.islands_below.empty()) // completely new island - needs support no doubt + uniformly_cover(*s.polygon, s, point_grid, true); else // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. - for (const ExPolygon& p : diff_ex(to_polygons(*s.polygon), offset(s.expolygons_below(), between_layers_offset))) + for (const ExPolygon& p : s.dangling_areas) //FIXME is it an island point or not? Vojtech thinks it is. - uniformly_cover(p, s); + uniformly_cover(p, s, point_grid); } // We should also check if current support is enough given the polygon area. - for (Structure& s : structures_new) { - // Areas not supported by the areas below. - ExPolygons e = diff_ex(to_polygons(*s.polygon), s.polygons_below()); - float e_area = 0.f; - for (const ExPolygon &ex : e) - e_area += float(ex.area()); + for (Structure& s : layer_top.islands) { // Penalization resulting from large diff from the last layer: // s.supports_force /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); - s.supports_force /= std::max(1.f, 0.17f * (e_area * float(SCALING_FACTOR * SCALING_FACTOR)) / s.area); - + s.supports_force /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); if (s.area * m_config.tear_pressure > s.supports_force) { //FIXME Don't calculate area inside the compare function! //FIXME Cover until the force deficit is covered. Cover multiple areas, sort by decreasing area. - 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()) + if (! s.overhangs.empty()) //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. - uniformly_cover(*largest_it, s); + uniformly_cover(s.overhangs.front(), s, point_grid); } } - // All is done. Prepare to advance to the next layer. - structures_old = std::move(structures_new); - structures_new.clear(); - m_throw_on_cancel(); #ifdef SLA_AUTOSUPPORTS_DEBUG @@ -251,7 +304,8 @@ std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float return out; } -std::vector poisson_disk_from_samples(const std::vector &raw_samples, float radius) +template +static inline std::vector poisson_disk_from_samples(const std::vector &raw_samples, float radius, REFUSE_FUNCTION refuse_function) { Vec2f corner_min(FLT_MAX, FLT_MAX); for (const Vec2f &pt : raw_samples) { @@ -334,7 +388,7 @@ std::vector poisson_disk_from_samples(const std::vector &raw_sampl const RawSample &candidate = raw_samples_sorted[next_sample_idx]; // See if this point conflicts with any other points in this cell, or with any points in // neighboring cells. Note that it's possible to have more than one point in the same cell. - bool conflict = false; + bool conflict = refuse_function(candidate.coord); for (int i = -1; i < 2 && ! conflict; ++ i) { for (int j = -1; j < 2; ++ j) { const auto &it_neighbor = cells.find(cell_id + Vec2i(i, j)); @@ -365,7 +419,7 @@ std::vector poisson_disk_from_samples(const std::vector &raw_sampl return out; } -void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& structure, bool is_new_island, bool just_one) +void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& structure, PointGrid3D &grid3d, 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)); @@ -374,12 +428,17 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct const float poisson_radius = 1.f / (5.f * density_horizontal); // const float poisson_radius = 1.f / (15.f * density_horizontal); const float samples_per_mm2 = 30.f / (float(M_PI) * poisson_radius * poisson_radius); + // Minimum distance between samples, in 3D space. + const float min_spacing = poisson_radius / 3.f; //FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon. std::random_device rd; std::mt19937 rng(rd()); std::vector raw_samples = sample_expolygon_with_boundary(island, samples_per_mm2, 5.f / poisson_radius, rng); - std::vector poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius); + std::vector poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, + [&structure, &grid3d, min_spacing](const Vec2f &pos) { + return grid3d.collides_with(pos, &structure, min_spacing); + }); #ifdef SLA_AUTOSUPPORTS_DEBUG { @@ -393,10 +452,11 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct } #endif /* NDEBUG */ - assert(! poisson_samples.empty()); +// assert(! poisson_samples.empty()); for (const Vec2f &pt : poisson_samples) { m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island); structure.supports_force += m_config.support_force; + grid3d.insert(pt, &structure); } } diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index fb5aceb4a..17f94bbd6 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -5,6 +5,8 @@ #include #include +#include + // #define SLA_AUTOSUPPORTS_DEBUG namespace Slic3r { @@ -24,22 +26,20 @@ public: const std::vector& heights, const Config& config, std::function throw_on_cancel); const std::vector& output() { return m_output; } -private: - std::vector m_output; - - SLAAutoSupports::Config m_config; + struct MyLayer; struct Structure { - Structure(const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) : polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h) + Structure(MyLayer &layer, const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) : + layer(&layer), polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h) #ifdef SLA_AUTOSUPPORTS_DEBUG , unique_id(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch())) #endif /* SLA_AUTOSUPPORTS_DEBUG */ {} + MyLayer *layer; const ExPolygon* polygon = nullptr; const BoundingBox bbox; const Vec2f centroid = Vec2f::Zero(); const float area = 0.f; - std::vector structures_below; float height = 0; // How well is this ExPolygon held to the print base? // Positive number, the higher the better. @@ -48,20 +48,26 @@ private: std::chrono::milliseconds unique_id; #endif /* SLA_AUTOSUPPORTS_DEBUG */ + boost::container::small_vector islands_above; + boost::container::small_vector islands_below; + ExPolygons dangling_areas; + ExPolygons overhangs; + float overhangs_area; + bool overlaps(const Structure &rhs) const { return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); } float area_below() const { float area = 0.f; - for (const Structure *below : this->structures_below) + for (const Structure *below : this->islands_below) area += below->area; return area; } Polygons polygons_below() const { size_t cnt = 0; - for (const Structure *below : this->structures_below) + for (const Structure *below : this->islands_below) cnt += 1 + below->polygon->holes.size(); Polygons out; out.reserve(cnt); - for (const Structure *below : this->structures_below) { + for (const Structure *below : this->islands_below) { out.emplace_back(below->polygon->contour); append(out, below->polygon->holes); } @@ -69,17 +75,87 @@ private: } ExPolygons expolygons_below() const { ExPolygons out; - out.reserve(this->structures_below.size()); - for (const Structure *below : this->structures_below) + out.reserve(this->islands_below.size()); + for (const Structure *below : this->islands_below) out.emplace_back(*below->polygon); return out; } }; + struct MyLayer { + MyLayer(const size_t layer_id, coordf_t print_z) : layer_id(layer_id), print_z(print_z) {} + size_t layer_id; + coordf_t print_z; + std::vector islands; + }; + + struct RichSupportPoint { + Vec3f position; + Structure *island; + }; + + struct PointGrid3D { + struct GridHash { + std::size_t operator()(const Vec3i &cell_id) { + return std::hash()(cell_id.x()) ^ std::hash()(cell_id.y() * 593) ^ std::hash()(cell_id.z() * 7919); + } + }; + typedef std::unordered_multimap Grid; + + Vec3f cell_size; + Grid grid; + + Vec3i cell_id(const Vec3f &pos) { + return Vec3i(int(floor(pos.x() / cell_size.x())), + int(floor(pos.y() / cell_size.y())), + int(floor(pos.z() / cell_size.z()))); + } + + void insert(const Vec2f &pos, Structure *island) { + RichSupportPoint pt; + pt.position = Vec3f(pos.x(), pos.y(), float(island->layer->print_z)); + pt.island = island; + grid.emplace(cell_id(pt.position), pt); + } + + bool collides_with(const Vec2f &pos, Structure *island, float radius) { + Vec3f pos3d(pos.x(), pos.y(), float(island->layer->print_z)); + Vec3i cell = cell_id(pos3d); + std::pair it_pair = grid.equal_range(cell); + if (collides_with(pos3d, radius, it_pair.first, it_pair.second)) + return true; + for (int i = -1; i < 2; ++ i) + for (int j = -1; j < 2; ++ j) + for (int k = -1; k < 1; ++ k) { + if (i == 0 && j == 0 && k == 0) + continue; + it_pair = grid.equal_range(cell + Vec3i(i, j, k)); + if (collides_with(pos3d, radius, it_pair.first, it_pair.second)) + return true; + } + return false; + } + + private: + bool collides_with(const Vec3f &pos, float radius, Grid::const_iterator it_begin, Grid::const_iterator it_end) { + for (Grid::const_iterator it = it_begin; it != it_end; ++ it) { + float dist2 = (it->second.position - pos).squaredNorm(); + if (dist2 < radius * radius) + return true; + } + return false; + } + }; + +private: + std::vector m_output; + + SLAAutoSupports::Config m_config; + float m_supports_force_total = 0.f; void process(const std::vector& slices, const std::vector& heights); - void uniformly_cover(const ExPolygon& island, Structure& structure, bool is_new_island = false, bool just_one = false); + void uniformly_cover(const ExPolygon& island, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); void project_onto_mesh(std::vector& points) const; #ifdef SLA_AUTOSUPPORTS_DEBUG From 5966dcb78e2aeab943819ce2697f31287f75aeb6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 15 Feb 2019 16:15:18 +0100 Subject: [PATCH 19/28] SLA gizmo - fixed support points rendering (depth is now correctly accounted for) --- src/libslic3r/SLA/SLAAutoSupports.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/GLGizmo.cpp | 81 +++++++++++++-------------- src/slic3r/GUI/GLGizmo.hpp | 4 +- 4 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 7321387af..4f7c6d49f 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -199,7 +199,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: float centroids_dist = (bottom->centroid - top.centroid).norm(); // Penalization resulting from centroid offset: // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); - bottom->supports_force *= std::min(1.f, 1.f - std::min(1.f, 80.f * centroids_dist * centroids_dist / bottom->area)); + bottom->supports_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom->area)); // Penalization resulting from increasing polygon area: bottom->supports_force *= std::min(1.f, 20.f * bottom->area / top.area); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 16811087e..43ce614fd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4598,9 +4598,9 @@ void GLCanvas3D::render() // this position is used later into on_mouse() to drag the objects m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast()); + _render_current_gizmo(); _render_selection_sidebar_hints(); - _render_current_gizmo(); #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); #endif // ENABLE_SHOW_CAMERA_TARGET diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 1d66d8e79..1e6205b58 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1874,68 +1874,58 @@ void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& sele void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, bool picking) const { - if (m_quadric == nullptr) + if (m_quadric == nullptr || !selection.is_from_single_instance()) return; - if (!selection.is_from_single_instance()) - return; - - const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_shift = v->get_sla_shift_z(); - - ::glPushMatrix(); - ::glTranslated(0.0, 0.0, z_shift); - - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - ::glMultMatrixd(m.data()); - if (!picking) ::glEnable(GL_LIGHTING); + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + double z_shift = vol->get_sla_shift_z(); + const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + + ::glPushMatrix(); + ::glTranslated(0.0, 0.0, z_shift); + ::glMultMatrixd(instance_matrix.data()); + float render_color[3]; for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i) { - const Vec3f& point_pos = m_editing_mode_cache[i].first.pos; - const bool point_selected = m_editing_mode_cache[i].second; - // first precalculate the grabber position in world coordinates, so that the grabber - // is not scaled with the object (as it would be if rendered with current gl matrix). - Eigen::Matrix glmatrix; - glGetFloatv (GL_MODELVIEW_MATRIX, glmatrix.data()); - Eigen::Matrix point_pos_4d; - for (int j=0; j<3; ++j) - point_pos_4d(j) = point_pos(j); - point_pos_4d[3] = 1.f; - Eigen::Matrix grabber_world_position = glmatrix * point_pos_4d; + const sla::SupportPoint& support_point = m_editing_mode_cache[i].first; + const bool& point_selected = m_editing_mode_cache[i].second; - if (!picking && (m_hover_id == i)) // point is in hover state - { - render_color[0] = 0.f; + // First decide about the color of the point. + if (picking) { + render_color[0] = 1.0f; render_color[1] = 1.0f; - render_color[2] = 1.0f; + render_color[2] = picking_color_component(i); } else { - if (picking) { - render_color[0] = 1.0f; + if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active + render_color[0] = 0.f; render_color[1] = 1.0f; - render_color[2] = picking_color_component(i); + render_color[2] = 1.0f; } - else { // normal rendering + else { // neigher hover nor picking bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].first.is_new_island; if (m_editing_mode) { - render_color[0] = point_selected ? 0.f : (supports_new_island ? 0.f : 1.f); - render_color[1] = point_selected ? 1.f : 0.f; - render_color[2] = point_selected ? 0.f : (supports_new_island ? 1.f : 0.f); + render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f); + render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f); + render_color[2] = point_selected ? 0.3f : (supports_new_island ? 1.0f : 0.7f); } else for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f; } } - ::glColor3fv(render_color); + + // Now render the sphere. Inverse matrix of the instance scaling is applied so that the + // sphere does not scale with the object. ::glPushMatrix(); - ::glLoadIdentity(); - ::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift); - ::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius, 64, 36); + ::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2)); + ::glMultMatrixd(instance_scaling_matrix_inverse.data()); + ::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius * RenderPointScale, 64, 36); ::glPopMatrix(); } @@ -2078,7 +2068,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous // Regardless of whether the user clicked the object or not, we will unselect all points: for (unsigned int i=0; i() * support_point.pos; pos(2) += z_offset; - GLdouble out_x, out_y, out_z; - ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); - out_y = m_canvas_height - out_y; + GLdouble out_x, out_y, out_z; + ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + out_y = m_canvas_height - out_y; if (rectangle.contains(Point(out_x, out_y))) point_and_selection.second = true; @@ -2382,7 +2372,12 @@ void GLGizmoSlaSupports::on_set_state() } if (m_state == Off) { m_parent.toggle_model_objects_visibility(true); + m_editing_mode_cache.clear(); + if (m_model_object) + for (const sla::SupportPoint& point : m_model_object->sla_support_points) + m_editing_mode_cache.push_back(std::make_pair(point, false)); m_editing_mode = false; + #if SLAGIZMO_IMGUI_MODAL if (m_show_modal) { m_show_modal = false; diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index b80916db7..2674f03f0 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -439,7 +439,7 @@ protected: -#define SLAGIZMO_IMGUI_MODAL 1 +#define SLAGIZMO_IMGUI_MODAL 0 class GLGizmoSlaSupports : public GLGizmoBase { private: @@ -449,6 +449,8 @@ private: int m_old_instance_id = -1; Vec3f unproject_on_mesh(const Vec2d& mouse_pos); + const float RenderPointScale = 1.f; + GLUquadricObj* m_quadric; Eigen::MatrixXf m_V; // vertices Eigen::MatrixXi m_F; // facets indices From dbfcb8b76edc93ae079c681efc20de7490f7b4c0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 18 Feb 2019 12:47:15 +0100 Subject: [PATCH 20/28] Small formal fixes on the new 3d grid algorithm --- src/libslic3r/SLA/SLAAutoSupports.cpp | 10 +++++----- src/libslic3r/SLA/SLAAutoSupports.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 4f7c6d49f..1d52dff6f 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -307,7 +307,7 @@ std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float template static inline std::vector poisson_disk_from_samples(const std::vector &raw_samples, float radius, REFUSE_FUNCTION refuse_function) { - Vec2f corner_min(FLT_MAX, FLT_MAX); + Vec2f corner_min(std::numeric_limits::max(), std::numeric_limits::max()); for (const Vec2f &pt : raw_samples) { corner_min.x() = std::min(corner_min.x(), pt.x()); corner_min.y() = std::min(corner_min.y(), pt.y()); @@ -342,7 +342,7 @@ static inline std::vector poisson_disk_from_samples(const std::vector()(cell_id.x()) ^ std::hash()(cell_id.y() * 593); } }; @@ -351,9 +351,9 @@ static inline std::vector poisson_disk_from_samples(const std::vector Cells; - std::unordered_map cells; + Cells cells; { - Cells::iterator last_cell_id_it; + typename Cells::iterator last_cell_id_it; Vec2i last_cell_id(-1, -1); for (int i = 0; i < raw_samples_sorted.size(); ++ i) { const RawSample &sample = raw_samples_sorted[i]; @@ -413,7 +413,7 @@ static inline std::vector poisson_disk_from_samples(const std::vector out; - for (const auto it : cells) + for (const auto& it : cells) for (int i = 0; i < it.second.num_poisson_samples; ++ i) out.emplace_back(it.second.poisson_samples[i]); return out; diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 17f94bbd6..979b75e4f 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -96,7 +96,7 @@ public: struct PointGrid3D { struct GridHash { - std::size_t operator()(const Vec3i &cell_id) { + std::size_t operator()(const Vec3i &cell_id) const { return std::hash()(cell_id.x()) ^ std::hash()(cell_id.y() * 593) ^ std::hash()(cell_id.z() * 7919); } }; From c63bdccb4bc326304480f748caeb70427b6a0078 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 18 Feb 2019 14:07:45 +0100 Subject: [PATCH 21/28] SLA gizmo selection rectangle now checks for obscured points --- src/slic3r/GUI/GLGizmo.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 1e6205b58..a1d1d1c91 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2101,6 +2101,13 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous // bounding box created from the rectangle corners - will take care of order of the corners BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); + const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true); + // we'll recover current look direction from the modelview matrix (in world coords)... + Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]); + // ...and transform it to model coords. + direction_to_camera = instance_matrix_no_translation.inverse().cast() * direction_to_camera.eval(); + + // Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh: for (std::pair& point_and_selection : m_editing_mode_cache) { const sla::SupportPoint& support_point = point_and_selection.first; Vec3f pos = instance_matrix.cast() * support_point.pos; @@ -2109,8 +2116,19 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); out_y = m_canvas_height - out_y; - if (rectangle.contains(Point(out_x, out_y))) - point_and_selection.second = true; + if (rectangle.contains(Point(out_x, out_y))) { + bool is_obscured = false; + // Cast a ray in the direction of the camera and look for intersection with the mesh: + std::vector hits; + if (m_AABB.intersect_ray(m_V, m_F, support_point.pos, direction_to_camera, hits)) + // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. + // Also, the threshold is in mesh coordinates, not in actual dimensions. + if (hits.size() > 1 || hits.front().t > 0.001f) + is_obscured = true; + + if (!is_obscured) + point_and_selection.second = true; + } } m_selection_rectangle_active = false; return true; From cb4763bb32f0817e1dc34847d0b0d36da7a0b969 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 18 Feb 2019 14:56:19 +0100 Subject: [PATCH 22/28] SLA gizmo - combobox items are now rendered immediately on mouse click, not after next mouse move --- src/slic3r/GUI/GLGizmo.cpp | 6 +++++- src/slic3r/GUI/GLGizmo.hpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 4 +++- src/slic3r/GUI/ImGuiWrapper.hpp | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index a1d1d1c91..73a65aedf 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2236,7 +2236,11 @@ RENDER_AGAIN: std::stringstream ss; ss << std::setprecision(1) << m_new_point_head_diameter; wxString str = ss.str(); - m_imgui->combo(_(L("Head diameter")), options, str); + + bool old_combo_state = m_combo_box_open; + m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options, str); + force_refresh |= (old_combo_state != m_combo_box_open); + float current_number = atof(str); if (std::abs(current_number - m_new_point_head_diameter) > 0.001) { force_refresh = true; diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 2674f03f0..1605700b1 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -498,6 +498,7 @@ private: Vec2d m_selection_rectangle_start_corner; Vec2d m_selection_rectangle_end_corner; bool m_ignore_up_event = false; + bool m_combo_box_open = false; #if SLAGIZMO_IMGUI_MODAL bool m_show_modal = false; #endif diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 3ce187a44..1eac4159f 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -168,7 +168,7 @@ void ImGuiWrapper::text(const wxString &label) } -void ImGuiWrapper::combo(const wxString& label, const std::vector& options, wxString& selection) +bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, wxString& selection) { std::string selection_u8 = into_u8(selection); @@ -184,7 +184,9 @@ void ImGuiWrapper::combo(const wxString& label, const std::vector& opt selection = option_u8; } ImGui::EndCombo(); + return true; } + return false; } void ImGuiWrapper::disabled_begin(bool disabled) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index a53718162..f85b70c21 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -52,7 +52,7 @@ public: 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); + bool combo(const wxString& label, const std::vector& options, wxString& current_selection); void disabled_begin(bool disabled); void disabled_end(); From 8b4bd7177bea648942edb84044bc7b17beeba357 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 19 Feb 2019 10:09:41 +0100 Subject: [PATCH 23/28] SLA auto supports: Work with support force deficit to sprinkle support points. Use OpenGL emissive material to render support points. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 108 +++++++++++++++++--------- src/libslic3r/SLA/SLAAutoSupports.hpp | 20 ++++- src/slic3r/GUI/GLGizmo.cpp | 8 ++ src/slic3r/GUI/SysInfoDialog.cpp | 2 + 4 files changed, 98 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 1d52dff6f..d1d122835 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -193,49 +193,54 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: point_grid.cell_size = Vec3f(10.f, 10.f, 10.f); for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) { - SLAAutoSupports::MyLayer &layer_top = layers[layer_id]; - for (Structure &top : layer_top.islands) + SLAAutoSupports::MyLayer *layer_top = &layers[layer_id]; + SLAAutoSupports::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr; + std::vector support_force_bottom; + if (layer_bottom != nullptr) { + support_force_bottom.assign(layer_bottom->islands.size(), 0.f); + for (size_t i = 0; i < layer_bottom->islands.size(); ++ i) + support_force_bottom[i] = layer_bottom->islands[i].supports_force_total(); + } + for (Structure &top : layer_top->islands) for (Structure *bottom : top.islands_below) { float centroids_dist = (bottom->centroid - top.centroid).norm(); // Penalization resulting from centroid offset: // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); - bottom->supports_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom->area)); + float &support_force = support_force_bottom[bottom - layer_bottom->islands.data()]; +//FIXME this condition does not reflect a bifurcation into a one large island and one tiny island well, it incorrectly resets the support force to zero. +// One should rather work with the overlap area vs overhang area. +// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom->area)); // Penalization resulting from increasing polygon area: - bottom->supports_force *= std::min(1.f, 20.f * bottom->area / top.area); + support_force *= std::min(1.f, 20.f * bottom->area / top.area); } // Let's assign proper support force to each of them: if (layer_id > 0) { - for (Structure &below : layers[layer_id - 1].islands) { + for (Structure &below : layer_bottom->islands) { + float below_support_force = support_force_bottom[&below - layer_bottom->islands.data()]; float above_area = 0.f; for (Structure *above : below.islands_above) above_area += above->area; for (Structure *above : below.islands_above) - above->supports_force += below.supports_force * above->area / above_area; + above->supports_force_inherited += below_support_force * above->area / above_area; } } // Now iterate over all polygons and append new points if needed. - for (Structure &s : layer_top.islands) { + for (Structure &s : layer_top->islands) { + // Penalization resulting from large diff from the last layer: +// s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); + s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); + + float force_deficit = s.support_force_deficit(m_config.tear_pressure); if (s.islands_below.empty()) // completely new island - needs support no doubt - uniformly_cover(*s.polygon, s, point_grid, true); - else + uniformly_cover({ *s.polygon }, s, point_grid, true); + else if (! s.dangling_areas.empty()) { // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. - for (const ExPolygon& p : s.dangling_areas) - //FIXME is it an island point or not? Vojtech thinks it is. - uniformly_cover(p, s, point_grid); - } - - // We should also check if current support is enough given the polygon area. - for (Structure& s : layer_top.islands) { - // Penalization resulting from large diff from the last layer: -// s.supports_force /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); - s.supports_force /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); - if (s.area * m_config.tear_pressure > s.supports_force) { - //FIXME Don't calculate area inside the compare function! - //FIXME Cover until the force deficit is covered. Cover multiple areas, sort by decreasing area. - if (! s.overhangs.empty()) - //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. - uniformly_cover(s.overhangs.front(), s, point_grid); + //FIXME is it an island point or not? Vojtech thinks it is. + uniformly_cover(s.dangling_areas, s, point_grid); + } else if (! s.overhangs.empty()) { + //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. + uniformly_cover(s.overhangs, s, point_grid); } } @@ -304,6 +309,14 @@ std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float return out; } +std::vector sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) +{ + std::vector out; + for (const ExPolygon &expoly : expolys) + append(out, sample_expolygon_with_boundary(expoly, samples_per_mm2, samples_per_mm_boundary, rng)); + return out; +} + template static inline std::vector poisson_disk_from_samples(const std::vector &raw_samples, float radius, REFUSE_FUNCTION refuse_function) { @@ -419,32 +432,51 @@ static inline std::vector poisson_disk_from_samples(const std::vector raw_samples = sample_expolygon_with_boundary(island, samples_per_mm2, 5.f / poisson_radius, rng); - std::vector poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, - [&structure, &grid3d, min_spacing](const Vec2f &pos) { - return grid3d.collides_with(pos, &structure, min_spacing); - }); + std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng); + std::vector poisson_samples; + for (size_t iter = 0; iter < 4; ++ iter) { + poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, + [&structure, &grid3d, min_spacing](const Vec2f &pos) { + return grid3d.collides_with(pos, &structure, min_spacing); + }); + if (poisson_samples.size() >= poisson_samples_target) + break; + float coeff = 0.5f; + if (poisson_samples.size() * 2 > poisson_samples_target) + coeff = float(poisson_samples.size()) / float(poisson_samples_target); + poisson_radius *= coeff; + min_spacing *= coeff; + } #ifdef SLA_AUTOSUPPORTS_DEBUG { static int irun = 0; Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(island)); - svg.draw(island); + for (const ExPolygon &island : islands) + svg.draw(island); for (const Vec2f &pt : raw_samples) svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red"); for (const Vec2f &pt : poisson_samples) @@ -453,9 +485,13 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct #endif /* NDEBUG */ // assert(! poisson_samples.empty()); + if (poisson_samples_target < poisson_samples.size()) { + std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng); + poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end()); + } for (const Vec2f &pt : poisson_samples) { m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island); - structure.supports_force += m_config.support_force; + structure.supports_force_this_layer += m_config.support_force; grid3d.insert(pt, &structure); } } diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 979b75e4f..bad408d04 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -18,8 +18,9 @@ public: float density_at_45; float minimal_z; /////////////// - float support_force = 30.f; // a force one point can support (arbitrary force unit) - float tear_pressure = 1.f; // pressure that the display exerts (the force unit per mm2) +// float support_force = 30.f; // a force one point can support (arbitrary force unit) + float support_force = 10.f; // a force one point can support (arbitrary force unit) + float tear_pressure = 1.f; // pressure that the display exerts (the force unit per mm2) }; SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, @@ -43,13 +44,22 @@ public: float height = 0; // How well is this ExPolygon held to the print base? // Positive number, the higher the better. - float supports_force = 0.f; + float supports_force_this_layer = 0.f; + float supports_force_inherited = 0.f; + float supports_force_total() const { return this->supports_force_this_layer + this->supports_force_inherited; } #ifdef SLA_AUTOSUPPORTS_DEBUG std::chrono::milliseconds unique_id; #endif /* SLA_AUTOSUPPORTS_DEBUG */ +#ifdef NDEBUG + // In release mode, use the optimized container. boost::container::small_vector islands_above; boost::container::small_vector islands_below; +#else + // In debug mode, use the standard vector, which is well handled by debugger visualizer. + std::vector islands_above; + std::vector islands_below; +#endif ExPolygons dangling_areas; ExPolygons overhangs; float overhangs_area; @@ -80,6 +90,8 @@ public: out.emplace_back(*below->polygon); return out; } + // Positive deficit of the supports. If negative, this area is well supported. If positive, more supports need to be added. + float support_force_deficit(const float tear_pressure) const { return this->area * tear_pressure - this->supports_force_total(); } }; struct MyLayer { @@ -155,7 +167,7 @@ private: float m_supports_force_total = 0.f; void process(const std::vector& slices, const std::vector& heights); - void uniformly_cover(const ExPolygon& island, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); + void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); void project_onto_mesh(std::vector& points) const; #ifdef SLA_AUTOSUPPORTS_DEBUG diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 73a65aedf..259fc4ca7 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1919,6 +1919,8 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b } } ::glColor3fv(render_color); + float render_color_emissive[4] = { 0.5 * render_color[0], 0.5 * render_color[1], 0.5 * render_color[2], 1.f}; + ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive); // Now render the sphere. Inverse matrix of the instance scaling is applied so that the // sphere does not scale with the object. @@ -1929,6 +1931,12 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b ::glPopMatrix(); } + { + // Reset emissive component to zero (the default value) + float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f }; + ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive); + } + if (!picking) ::glDisable(GL_LIGHTING); diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index fd8ab5f93..8f7c65d7e 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -3,6 +3,8 @@ #include "3DScene.hpp" #include "GUI.hpp" +#include + #include #include From 3c7912186ebc910d450bd89a4ab58f9b18adc182 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 19 Feb 2019 10:46:21 +0100 Subject: [PATCH 24/28] SLA gizmo cannot be activated for objects outside the bed --- src/slic3r/GUI/GLGizmo.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 259fc4ca7..27c63311b 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2250,12 +2250,14 @@ RENDER_AGAIN: force_refresh |= (old_combo_state != m_combo_box_open); float current_number = atof(str); - if (std::abs(current_number - m_new_point_head_diameter) > 0.001) { - force_refresh = true; - m_new_point_head_diameter = current_number; + if (old_combo_state && !m_combo_box_open) // closing the combo must always change the sizes (even if the selection did not change) for (auto& point_and_selection : m_editing_mode_cache) if (point_and_selection.second) point_and_selection.first.head_front_radius = current_number / 2.f; + + if (std::abs(current_number - m_new_point_head_diameter) > 0.001) { + force_refresh = true; + m_new_point_head_diameter = current_number; } bool changed = m_lock_unique_islands; @@ -2376,8 +2378,17 @@ RENDER_AGAIN: bool GLGizmoSlaSupports::on_is_activable(const GLCanvas3D::Selection& selection) const { - return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) - && selection.is_from_single_instance(); + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA + || !selection.is_from_single_instance()) + return false; + + // Check that none of the selected volumes is outside. + const GLCanvas3D::Selection::IndicesList& list = selection.get_volume_idxs(); + for (const auto& idx : list) + if (selection.get_volume(idx)->is_outside) + return false; + + return true; } bool GLGizmoSlaSupports::on_is_selectable() const From 1bb0af1588f4f674033abeaf8fc8acbd86b98bcd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 19 Feb 2019 13:47:28 +0100 Subject: [PATCH 25/28] SLA gizmo asks about saving changes, right button click removes points --- src/libslic3r/SLA/SLACommon.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 9 +- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GLGizmo.cpp | 168 +++++++++++++++++++++----------- src/slic3r/GUI/GLGizmo.hpp | 14 +++ src/slic3r/GUI/Plater.cpp | 2 +- 6 files changed, 135 insertions(+), 60 deletions(-) diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index ab535fb9e..f7c0acf33 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -33,6 +33,7 @@ struct SupportPoint { pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4) != 0.f) {} bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; } + bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); } }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b4d050d27..0e1f06168 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3158,6 +3158,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); float top_y = 0.5f * (cnv_h - height) + m_overlay_border; + for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) @@ -5448,10 +5449,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } - /*else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse() && evt.RightDown()) { - if (m_gizmos.get_current_type() == Gizmos::SlaSupports) - m_gizmos.delete_current_grabber(); - }*/ + else if ((selected_object_idx != -1) && evt.RightDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::RightDown)) + { + // event was taken care of by the SlaSupports gizmo + } else if (view_toolbar_contains_mouse != -1) { if (m_view_toolbar != nullptr) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ff69f1ef8..6b3839b4f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -136,6 +136,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); enum class SLAGizmoEventType { LeftDown = 1, LeftUp, + RightDown, Dragging, Delete, SelectAll, diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 27c63311b..84609791d 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1784,23 +1784,15 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G m_active_instance = selection.get_instance_idx(); - if ((model_object != nullptr) && selection.is_from_single_instance()) + if (model_object && selection.is_from_single_instance()) { if (is_mesh_update_necessary()) update_mesh(); // If there are no points, let's ask the backend if it calculated some. - if (m_editing_mode_cache.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) { - for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == model_object->id()) { - const std::vector& points = po->get_support_points(); - auto mat = po->trafo().inverse().cast(); - for (unsigned int i=0; iis_step_done(slaposSupportPoints)) + get_data_from_backend(); + if (m_model_object != m_old_model_object) m_editing_mode = false; if (m_state == On) { @@ -1919,7 +1911,7 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b } } ::glColor3fv(render_color); - float render_color_emissive[4] = { 0.5 * render_color[0], 0.5 * render_color[1], 0.5 * render_color[2], 1.f}; + float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; ::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive); // Now render the sphere. Inverse matrix of the instance scaling is applied so that the @@ -1981,9 +1973,7 @@ void GLGizmoSlaSupports::update_mesh() m_AABB.init(m_V, m_F); // we'll now reload support points (selection might have changed): - m_editing_mode_cache.clear(); - for (const sla::SupportPoint& point : m_model_object->sla_support_points) - m_editing_mode_cache.push_back(std::make_pair(point, false)); + editing_mode_reload_cache(); } Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) @@ -2045,7 +2035,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous m_canvas_height = m_parent.get_canvas_size().get_height(); } else - m_editing_mode_cache[m_hover_id].second = true; + select_point(m_hover_id); return true; } @@ -2075,13 +2065,13 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous return false; // Regardless of whether the user clicked the object or not, we will unselect all points: - for (unsigned int i=0; ireslice(); } @@ -2182,6 +2183,7 @@ void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Sel catch (...) { return; } m_editing_mode_cache[m_hover_id].first.pos = new_pos; m_editing_mode_cache[m_hover_id].first.is_new_island = false; + m_unsaved_changes = true; // Do not update immediately, wait until the mouse is released. // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } @@ -2237,6 +2239,7 @@ RENDER_AGAIN: 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(_(L("Shift + Left (+ drag) - select point(s)"))); m_imgui->text(" "); // vertical gap @@ -2252,8 +2255,10 @@ RENDER_AGAIN: float current_number = atof(str); if (old_combo_state && !m_combo_box_open) // closing the combo must always change the sizes (even if the selection did not change) for (auto& point_and_selection : m_editing_mode_cache) - if (point_and_selection.second) + if (point_and_selection.second) { point_and_selection.first.head_front_radius = current_number / 2.f; + m_unsaved_changes = true; + } if (std::abs(current_number - m_new_point_head_diameter) > 0.001) { force_refresh = true; @@ -2270,20 +2275,14 @@ RENDER_AGAIN: bool apply_changes = m_imgui->button(_(L("Apply changes"))); if (apply_changes) { - m_model_object->sla_support_points.clear(); - for (const std::pair& point_and_selection : m_editing_mode_cache) - m_model_object->sla_support_points.push_back(point_and_selection.first); - m_editing_mode = false; + editing_mode_apply_changes(); force_refresh = true; m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } ImGui::SameLine(); bool discard_changes = m_imgui->button(_(L("Discard changes"))); if (discard_changes) { - m_editing_mode_cache.clear(); - for (const sla::SupportPoint& point : m_model_object->sla_support_points) - m_editing_mode_cache.push_back(std::make_pair(point, false)); - m_editing_mode = false; + editing_mode_discard_changes(); force_refresh = true; } } @@ -2304,21 +2303,21 @@ RENDER_AGAIN: force_refresh = true; #else wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( - "Autogeneration will erase all currently assigned points.\n\n" + "Autogeneration will erase all manually edited points.\n\n" "Are you sure you want to do it?\n" )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) { - m_model_object->sla_support_points.clear(); - m_editing_mode_cache.clear(); - wxGetApp().plater()->reslice(); - } + if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) { + m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); + wxGetApp().plater()->reslice(); + } #endif } #if SLAGIZMO_IMGUI_MODAL if (m_show_modal) { if (ImGui::BeginPopupModal(_(L("Warning")), &m_show_modal/*, ImGuiWindowFlags_NoDecoration*/)) { - m_imgui->text(_(L("Autogeneration will erase all currently assigned points."))); + m_imgui->text(_(L("Autogeneration will erase all manually edited points."))); m_imgui->text(""); m_imgui->text(_(L("Are you sure you want to do it?"))); @@ -2346,9 +2345,7 @@ RENDER_AGAIN: ImGui::SameLine(); bool editing_clicked = m_imgui->button("Editing"); if (editing_clicked) { - m_editing_mode_cache.clear(); - for (const sla::SupportPoint& point : m_model_object->sla_support_points) - m_editing_mode_cache.push_back(std::make_pair(point, false)); + editing_mode_reload_cache(); m_editing_mode = true; } } @@ -2412,29 +2409,90 @@ void GLGizmoSlaSupports::on_set_state() m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); } if (m_state == Off) { - m_parent.toggle_model_objects_visibility(true); - m_editing_mode_cache.clear(); - if (m_model_object) - for (const sla::SupportPoint& point : m_model_object->sla_support_points) - m_editing_mode_cache.push_back(std::make_pair(point, false)); - m_editing_mode = false; + if (m_old_state != Off && m_model_object) { // the gizmo was just turned Off + + if (m_unsaved_changes) { + wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")), + _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO); + if (dlg.ShowModal() == wxID_YES) + editing_mode_apply_changes(); + else + editing_mode_discard_changes(); + } + + m_parent.toggle_model_objects_visibility(true); + m_editing_mode = false; // so it is not active next time the gizmo opens #if SLAGIZMO_IMGUI_MODAL - if (m_show_modal) { - m_show_modal = false; - on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly - } + if (m_show_modal) { + m_show_modal = false; + on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly + } #endif + } } + m_old_state = m_state; } void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection) { - if (m_hover_id != -1) - for (unsigned int i=0;isla_support_points) + m_editing_mode_cache.push_back(std::make_pair(point, false)); + m_editing_mode = false; + m_unsaved_changes = false; +} + +void GLGizmoSlaSupports::editing_mode_apply_changes() +{ + m_model_object->sla_support_points.clear(); + for (const std::pair& point_and_selection : m_editing_mode_cache) + m_model_object->sla_support_points.push_back(point_and_selection.first); + m_editing_mode = false; + m_unsaved_changes = false; +} + +void GLGizmoSlaSupports::editing_mode_reload_cache() +{ + m_editing_mode_cache.clear(); + for (const sla::SupportPoint& point : m_model_object->sla_support_points) + m_editing_mode_cache.push_back(std::make_pair(point, false)); + m_unsaved_changes = false; +} + +void GLGizmoSlaSupports::get_data_from_backend() +{ + for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { + if (po->model_object()->id() == m_model_object->id()) { + const std::vector& points = po->get_support_points(); + auto mat = po->trafo().inverse().cast(); + for (unsigned int i=0; icanvas3D()->reload_scene(true); } } From eb0fd03861fc9c2e4e62f3430ee19675e8c1e2b5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 19 Feb 2019 16:34:52 +0100 Subject: [PATCH 26/28] SLA supports - added parameters for the automatic generation (relative density measure and minimal distance of the points) --- src/libslic3r/PrintConfig.cpp | 25 ++++++++---------------- src/libslic3r/PrintConfig.hpp | 10 ++++------ src/libslic3r/SLA/SLAAutoSupports.cpp | 18 ++++++++--------- src/libslic3r/SLA/SLAAutoSupports.hpp | 10 ++++------ src/libslic3r/SLAPrint.cpp | 10 ++++++---- src/slic3r/GUI/GLGizmo.cpp | 28 ++++++++++++++++----------- src/slic3r/GUI/GLGizmo.hpp | 2 ++ src/slic3r/GUI/Preset.cpp | 5 ++--- src/slic3r/GUI/Tab.cpp | 5 ++--- 9 files changed, 54 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3e1ee9f9f..c01a1f6aa 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2630,28 +2630,19 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->default_value = new ConfigOptionFloat(5.0); - def = this->add("support_density_at_horizontal", coInt); - def->label = L("Density on horizontal surfaces"); + def = this->add("support_points_density_relative", coInt); + def->label = L("Support points density"); def->category = L("Supports"); - def->tooltip = L("How many support points (approximately) should be placed on horizontal surface."); - def->sidetext = L("points per square dm"); + def->tooltip = L("This is a relative measure of support points density."); + def->sidetext = L("%"); def->cli = ""; def->min = 0; - def->default_value = new ConfigOptionInt(500); + def->default_value = new ConfigOptionInt(100); - def = this->add("support_density_at_45", coInt); - def->label = L("Density on surfaces at 45 degrees"); + def = this->add("support_points_minimal_distance", coFloat); + def->label = L("Minimal distance of the support points"); def->category = L("Supports"); - def->tooltip = L("How many support points (approximately) should be placed on surface sloping at 45 degrees."); - def->sidetext = L("points per square dm"); - def->cli = ""; - def->min = 0; - def->default_value = new ConfigOptionInt(250); - - def = this->add("support_minimal_z", coFloat); - def->label = L("Minimal support point height"); - def->category = L("Supports"); - def->tooltip = L("No support points will be placed lower than this value from the bottom."); + def->tooltip = L("No support points will be placed closer than this threshold."); def->sidetext = L("mm"); def->cli = ""; def->min = 0; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 20c089d1c..c6c917515 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1002,9 +1002,8 @@ public: ConfigOptionFloat support_object_elevation /*= 5.0*/; /////// Following options influence automatic support points placement: - ConfigOptionInt support_density_at_horizontal; - ConfigOptionInt support_density_at_45; - ConfigOptionFloat support_minimal_z; + ConfigOptionInt support_points_density_relative; + ConfigOptionFloat support_points_minimal_distance; // Now for the base pool (pad) ///////////////////////////////////////////// @@ -1040,9 +1039,8 @@ protected: OPT_PTR(support_base_height); OPT_PTR(support_critical_angle); OPT_PTR(support_max_bridge_length); - OPT_PTR(support_density_at_horizontal); - OPT_PTR(support_density_at_45); - OPT_PTR(support_minimal_z); + OPT_PTR(support_points_density_relative); + OPT_PTR(support_points_minimal_distance); OPT_PTR(support_object_elevation); OPT_PTR(pad_enable); OPT_PTR(pad_wall_thickness); diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index d1d122835..25c070984 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -230,7 +230,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: // s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); - float force_deficit = s.support_force_deficit(m_config.tear_pressure); + float force_deficit = s.support_force_deficit(m_config.tear_pressure()); if (s.islands_below.empty()) // completely new island - needs support no doubt uniformly_cover({ *s.polygon }, s, point_grid, true); else if (! s.dangling_areas.empty()) { @@ -436,16 +436,16 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru { //int num_of_points = std::max(1, (int)((island.area()*pow(SCALING_FACTOR, 2) * m_config.tear_pressure)/m_config.support_force)); - const float support_force_deficit = structure.support_force_deficit(m_config.tear_pressure); + const float support_force_deficit = structure.support_force_deficit(m_config.tear_pressure()); if (support_force_deficit < 0) return; // Number of newly added points. - const size_t poisson_samples_target = size_t(ceil(support_force_deficit / m_config.support_force)); + const size_t poisson_samples_target = size_t(ceil(support_force_deficit / m_config.support_force())); - const float density_horizontal = m_config.tear_pressure / m_config.support_force; + const float density_horizontal = m_config.tear_pressure() / m_config.support_force(); //FIXME why? - float poisson_radius = 1.f / (5.f * density_horizontal); + float poisson_radius = std::max(m_config.minimal_distance, 1.f / (5.f * density_horizontal)); // const float poisson_radius = 1.f / (15.f * density_horizontal); const float samples_per_mm2 = 30.f / (float(M_PI) * poisson_radius * poisson_radius); // Minimum distance between samples, in 3D space. @@ -462,13 +462,13 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru [&structure, &grid3d, min_spacing](const Vec2f &pos) { return grid3d.collides_with(pos, &structure, min_spacing); }); - if (poisson_samples.size() >= poisson_samples_target) + if (poisson_samples.size() >= poisson_samples_target || m_config.minimal_distance > poisson_radius-EPSILON) break; float coeff = 0.5f; if (poisson_samples.size() * 2 > poisson_samples_target) coeff = float(poisson_samples.size()) / float(poisson_samples_target); - poisson_radius *= coeff; - min_spacing *= coeff; + poisson_radius = std::max(m_config.minimal_distance, poisson_radius * coeff); + min_spacing = std::max(m_config.minimal_distance, min_spacing * coeff); } #ifdef SLA_AUTOSUPPORTS_DEBUG @@ -491,7 +491,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru } for (const Vec2f &pt : poisson_samples) { m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island); - structure.supports_force_this_layer += m_config.support_force; + structure.supports_force_this_layer += m_config.support_force(); grid3d.insert(pt, &structure); } } diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index bad408d04..0cf810626 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -14,13 +14,11 @@ namespace Slic3r { class SLAAutoSupports { public: struct Config { - float density_at_horizontal; - float density_at_45; - float minimal_z; + float density_relative; + float minimal_distance; /////////////// -// float support_force = 30.f; // a force one point can support (arbitrary force unit) - float support_force = 10.f; // a force one point can support (arbitrary force unit) - float tear_pressure = 1.f; // pressure that the display exerts (the force unit per mm2) + inline float support_force() const { return 10.f / density_relative; } // a force one point can support (arbitrary force unit) + inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2) }; SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 4865150c2..d0c6bfdef 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -532,9 +532,8 @@ void SLAPrint::process() this->throw_if_canceled(); SLAAutoSupports::Config config; const SLAPrintObjectConfig& cfg = po.config(); - config.minimal_z = float(cfg.support_minimal_z); - config.density_at_45 = cfg.support_density_at_45 / 10000.f; - config.density_at_horizontal = cfg.support_density_at_horizontal / 10000.f; + config.density_relative = (float)cfg.support_points_density_relative / 100.f; // the config value is in percents + config.minimal_distance = cfg.support_points_minimal_distance; // Construction of this object does the calculation. this->throw_if_canceled(); @@ -1064,7 +1063,10 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectortext("Some settings could"); - //m_imgui->text("be exposed here..."); - m_imgui->text(""); - m_imgui->text(""); - - m_imgui->text(""); + /* ImGui::PushItemWidth(50.0f); + m_imgui->text(_(L("Minimal points distance: "))); + ImGui::SameLine(); + bool value_changed = ImGui::InputDouble("mm", &m_minimal_point_distance, 0.0f, 0.0f, "%.2f"); + m_imgui->text(_(L("Support points density: "))); + ImGui::SameLine(); + value_changed |= ImGui::InputDouble("%", &m_density, 0.0f, 0.0f, "%.f");*/ bool generate = m_imgui->button(_(L("Auto-generate points"))); @@ -2342,8 +2343,9 @@ RENDER_AGAIN: force_refresh = true; } #endif - ImGui::SameLine(); - bool editing_clicked = m_imgui->button("Editing"); + m_imgui->text(""); + m_imgui->text(""); + bool editing_clicked = m_imgui->button(_(L("Manual editing"))); if (editing_clicked) { editing_mode_reload_cache(); m_editing_mode = true; @@ -2463,9 +2465,13 @@ void GLGizmoSlaSupports::editing_mode_discard_changes() void GLGizmoSlaSupports::editing_mode_apply_changes() { - m_model_object->sla_support_points.clear(); - for (const std::pair& point_and_selection : m_editing_mode_cache) - m_model_object->sla_support_points.push_back(point_and_selection.first); + // If there are no changes, don't touch the front-end. The data in the cache could have been + // taken from the backend and copying them to ModelObject would needlessly invalidate them. + if (m_unsaved_changes) { + m_model_object->sla_support_points.clear(); + for (const std::pair& point_and_selection : m_editing_mode_cache) + m_model_object->sla_support_points.push_back(point_and_selection.first); + } m_editing_mode = false; m_unsaved_changes = false; } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 2be969c00..97e3d0c6f 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -492,6 +492,8 @@ private: bool m_lock_unique_islands = false; bool m_editing_mode = false; float m_new_point_head_diameter = 0.4f; + double m_minimal_point_distance = 20.; + double m_density = 100.; std::vector> m_editing_mode_cache; // a support point and whether it is currently selected bool m_selection_rectangle_active = false; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 19626847f..ab6d3275a 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -457,9 +457,8 @@ const std::vector& Preset::sla_print_options() "support_critical_angle", "support_max_bridge_length", "support_object_elevation", - "support_density_at_horizontal", - "support_density_at_45", - "support_minimal_z", + "support_points_density_relative", + "support_points_minimal_distance", "pad_enable", "pad_wall_thickness", "pad_wall_height", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 28b7cd248..788276357 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3202,9 +3202,8 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_max_bridge_length"); optgroup = page->new_optgroup(_(L("Automatic generation"))); - optgroup->append_single_option_line("support_density_at_horizontal"); - optgroup->append_single_option_line("support_density_at_45"); - optgroup->append_single_option_line("support_minimal_z"); + optgroup->append_single_option_line("support_points_density_relative"); + optgroup->append_single_option_line("support_points_minimal_distance"); page = add_options_page(_(L("Pad")), "brick.png"); optgroup = page->new_optgroup(_(L("Pad"))); From 4558910a84c53e90010993f0641b3b98689feb68 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 20 Feb 2019 09:59:59 +0100 Subject: [PATCH 27/28] SLA gizmo can be deselected by clicking in canvas area (unless in editing mode) --- src/slic3r/GUI/GLCanvas3D.cpp | 3 ++- src/slic3r/GUI/GLGizmo.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0e1f06168..98d7b43a1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5692,9 +5692,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); } else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() - && !is_layers_editing_enabled() && m_gizmos.get_current_type() != Gizmos::SlaSupports) + && !is_layers_editing_enabled() && (m_gizmos.get_current_type() != Gizmos::SlaSupports || !m_gizmos.mouse_event(SLAGizmoEventType::LeftUp, Vec2d(pos(0), pos(1)), evt.ShiftDown()))) { // SLA gizmo cannot be deselected by clicking in canvas area to avoid inadvertent unselection and losing manual changes + // that's why the mouse_event function was called so that the gizmo can refuse the deselection in manual editing mode // deselect and propagate event through callback if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 23c8187a5..e7b31e309 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2073,8 +2073,8 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), true)); m_unsaved_changes = true; } - catch (...) { // not clicked on object - return false; // GLCanvas3D might want to deselect the gizmo + catch (...) { // not clicked on object + return true; // prevents deselection of the gizmo by GLCanvas3D } return true; } From 5499bbd1da063daf2e4151269f7a7ba686dec55a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 20 Feb 2019 10:46:49 +0100 Subject: [PATCH 28/28] SLA auto supports: Improved distribution of the support forces in the up direction by taking into account island overlap areas. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 37 ++++++++++--------- src/libslic3r/SLA/SLAAutoSupports.hpp | 51 ++++++++++++++++++--------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index d1d122835..e8cb940b7 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -146,11 +146,13 @@ static std::vector make_layers( const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. for (SLAAutoSupports::Structure &top : layer_above.islands) { - for (SLAAutoSupports::Structure &bottom : layer_below.islands) - if (top.overlaps(bottom)) { - top.islands_below.emplace_back(&bottom); - bottom.islands_above.emplace_back(&top); + for (SLAAutoSupports::Structure &bottom : layer_below.islands) { + float overlap_area = top.overlap_area(bottom); + if (overlap_area > 0) { + top.islands_below.emplace_back(&bottom, overlap_area); + bottom.islands_above.emplace_back(&top, overlap_area); } + } if (! top.islands_below.empty()) { Polygons top_polygons = to_polygons(*top.polygon); Polygons bottom_polygons = top.polygons_below(); @@ -202,26 +204,27 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: support_force_bottom[i] = layer_bottom->islands[i].supports_force_total(); } for (Structure &top : layer_top->islands) - for (Structure *bottom : top.islands_below) { - float centroids_dist = (bottom->centroid - top.centroid).norm(); + for (Structure::Link &bottom_link : top.islands_below) { + Structure &bottom = *bottom_link.island; + float centroids_dist = (bottom.centroid - top.centroid).norm(); // Penalization resulting from centroid offset: // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); - float &support_force = support_force_bottom[bottom - layer_bottom->islands.data()]; + float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()]; //FIXME this condition does not reflect a bifurcation into a one large island and one tiny island well, it incorrectly resets the support force to zero. // One should rather work with the overlap area vs overhang area. -// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom->area)); +// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom.area)); // Penalization resulting from increasing polygon area: - support_force *= std::min(1.f, 20.f * bottom->area / top.area); + support_force *= std::min(1.f, 20.f * bottom.area / top.area); } // Let's assign proper support force to each of them: if (layer_id > 0) { for (Structure &below : layer_bottom->islands) { float below_support_force = support_force_bottom[&below - layer_bottom->islands.data()]; - float above_area = 0.f; - for (Structure *above : below.islands_above) - above_area += above->area; - for (Structure *above : below.islands_above) - above->supports_force_inherited += below_support_force * above->area / above_area; + float above_overlap_area = 0.f; + for (Structure::Link &above_link : below.islands_above) + above_overlap_area += above_link.overlap_area; + for (Structure::Link &above_link : below.islands_above) + above_link.island->supports_force_inherited += below_support_force * above_link.overlap_area / above_overlap_area; } } // Now iterate over all polygons and append new points if needed. @@ -231,9 +234,9 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); float force_deficit = s.support_force_deficit(m_config.tear_pressure); - if (s.islands_below.empty()) // completely new island - needs support no doubt + if (s.islands_below.empty()) { // completely new island - needs support no doubt uniformly_cover({ *s.polygon }, s, point_grid, true); - else if (! s.dangling_areas.empty()) { + } else if (! s.dangling_areas.empty()) { // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. //FIXME is it an island point or not? Vojtech thinks it is. @@ -474,7 +477,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru #ifdef SLA_AUTOSUPPORTS_DEBUG { static int irun = 0; - Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(island)); + Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(islands)); for (const ExPolygon &island : islands) svg.draw(island); for (const Vec2f &pt : raw_samples) diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index bad408d04..f43cc9faa 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -51,43 +51,60 @@ public: std::chrono::milliseconds unique_id; #endif /* SLA_AUTOSUPPORTS_DEBUG */ + struct Link { + Link(Structure *island, float overlap_area) : island(island), overlap_area(overlap_area) {} + Structure *island; + float overlap_area; + }; + #ifdef NDEBUG // In release mode, use the optimized container. - boost::container::small_vector islands_above; - boost::container::small_vector islands_below; + boost::container::small_vector islands_above; + boost::container::small_vector islands_below; #else // In debug mode, use the standard vector, which is well handled by debugger visualizer. - std::vector islands_above; - std::vector islands_below; + std::vector islands_above; + std::vector islands_below; #endif - ExPolygons dangling_areas; - ExPolygons overhangs; - float overhangs_area; + ExPolygons dangling_areas; + ExPolygons overhangs; + float overhangs_area; - bool overlaps(const Structure &rhs) const { return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); } + bool overlaps(const Structure &rhs) const { + return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); + } + float overlap_area(const Structure &rhs) const { + double out = 0.; + if (this->bbox.overlap(rhs.bbox)) { + Polygons polys = intersection(to_polygons(*this->polygon), to_polygons(*rhs.polygon), false); + for (const Polygon &poly : polys) + out += poly.area(); + } + return float(out); + } float area_below() const { float area = 0.f; - for (const Structure *below : this->islands_below) - area += below->area; + for (const Link &below : this->islands_below) + area += below.island->area; return area; } Polygons polygons_below() const { size_t cnt = 0; - for (const Structure *below : this->islands_below) - cnt += 1 + below->polygon->holes.size(); + for (const Link &below : this->islands_below) + cnt += 1 + below.island->polygon->holes.size(); Polygons out; out.reserve(cnt); - for (const Structure *below : this->islands_below) { - out.emplace_back(below->polygon->contour); - append(out, below->polygon->holes); + for (const Link &below : this->islands_below) { + out.emplace_back(below.island->polygon->contour); + append(out, below.island->polygon->holes); } return out; } ExPolygons expolygons_below() const { ExPolygons out; out.reserve(this->islands_below.size()); - for (const Structure *below : this->islands_below) - out.emplace_back(*below->polygon); + for (const Link &below : this->islands_below) + out.emplace_back(*below.island->polygon); return out; } // Positive deficit of the supports. If negative, this area is well supported. If positive, more supports need to be added.