Vojtech's optimizations

This commit is contained in:
bubnikv 2019-02-06 10:57:45 +01:00
parent 9dff44a8ad
commit 0e8b526af3
3 changed files with 137 additions and 105 deletions

View file

@ -1,6 +1,8 @@
#include "igl/random_points_on_mesh.h" #include "igl/random_points_on_mesh.h"
#include "igl/AABB.h" #include "igl/AABB.h"
#include <tbb/parallel_for.h>
#include "SLAAutoSupports.hpp" #include "SLAAutoSupports.hpp"
#include "Model.hpp" #include "Model.hpp"
#include "ExPolygon.hpp" #include "ExPolygon.hpp"
@ -58,42 +60,52 @@ void SLAAutoSupports::project_onto_mesh(std::vector<sla::SupportPoint>& points)
igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f}; igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f};
igl::Hit hit_down{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) { // Use a reasonable granularity to account for the worker thread synchronization cost.
Vec3f& p = support_point.pos; tbb::parallel_for(tbb::blocked_range<size_t>(0, points.size(), 64),
// Project the point upward and downward and choose the closer intersection with the mesh. [this, &points](const tbb::blocked_range<size_t>& range) {
//bool up = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., 1.), m_V, m_F, hit_up); for (size_t point_id = range.begin(); point_id < range.end(); ++ point_id) {
//bool down = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., -1.), m_V, m_F, hit_down); 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<float>(), Vec3f(0., 0., 1.), m_V, m_F, hit_up);
//bool down = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., -1.), m_V, m_F, hit_down);
sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast<double>(), Vec3d(0., 0., 1.)); sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast<double>(), Vec3d(0., 0., 1.));
sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast<double>(), Vec3d(0., 0., -1.)); sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast<double>(), Vec3d(0., 0., -1.));
bool up = hit_up.face() != -1; bool up = hit_up.face() != -1;
bool down = hit_down.face() != -1; bool down = hit_down.face() != -1;
if (!up && !down) if (!up && !down)
continue; continue;
sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down;
//int fid = hit.face(); //int fid = hit.face();
//Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); //Vec3f bc(1-hit.u-hit.v, hit.u, hit.v);
//p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast<float>(); //p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast<float>();
p = p + (hit.distance() * hit.direction()).cast<float>(); p = p + (hit.distance() * hit.direction()).cast<float>();
} }
});
} }
void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights)
{ {
std::vector<std::pair<ExPolygon, coord_t>> islands; std::vector<std::pair<ExPolygon, coord_t>> islands;
std::vector<Structure> structures_old;
std::vector<Structure> structures_new;
for (unsigned int i = 0; i<slices.size(); ++i) { for (unsigned int i = 0; i<slices.size(); ++i) {
const ExPolygons& expolys_top = slices[i]; const ExPolygons& expolys_top = slices[i];
//FIXME WTF?
const float height = (i>2 ? heights[i-3] : heights[0]-(heights[1]-heights[0])); 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 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 safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
const float offset = scale_(layer_height / std::tan(safe_angle)); const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
// FIXME: calculate actual pixel area from printer config: // FIXME: calculate actual pixel area from printer config:
//const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") / wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); // //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") / wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); //
@ -101,87 +113,76 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
// Check all ExPolygons on this slice and check whether they are new or belonging to something below. // Check all ExPolygons on this slice and check whether they are new or belonging to something below.
for (const ExPolygon& polygon : expolys_top) { 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; continue;
//FIXME this is not a correct centroid of a polygon with holes.
m_structures_new.emplace_back(polygon, height); structures_new.emplace_back(polygon, get_extents(polygon.contour), Slic3r::unscale(polygon.contour.centroid()).cast<float>(), area, height);
for (Structure& s : m_structures_old) { Structure& top = structures_new.back();
const ExPolygon* bottom = s.polygon; //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands.
if (polygon.overlaps(*bottom) || bottom->overlaps(polygon)) { // At least it is now using a bounding box check for pre-filtering.
m_structures_new.back().structures_below.push_back(&s); for (Structure& bottom : structures_old)
if (top.overlaps(bottom)) {
coord_t centroids_dist = (bottom->contour.centroid() - polygon.contour.centroid()).norm(); top.structures_below.push_back(&bottom);
float centroids_dist = (bottom.centroid - top.centroid).norm();
// Penalization resulting from centroid offset: // 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: // 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: // Let's assign proper support force to each of them:
for (Structure& old_str : m_structures_old) { for (const Structure& below : structures_old) {
std::vector<Structure*> children; std::vector<Structure*> above_list;
float children_area = 0.f; float above_area = 0.f;
for (Structure& new_str : m_structures_new) for (Structure& new_str : structures_new)
for (const Structure* below : new_str.structures_below) for (const Structure* below1 : new_str.structures_below)
if (&old_str == below) { if (&below == below1) {
children.push_back(&new_str); above_list.push_back(&new_str);
children_area += children.back()->polygon->area() * pow(SCALING_FACTOR, 2); above_area += above_list.back()->area;
} }
for (Structure* child : children) for (Structure* above : above_list)
child->supports_force += (old_str.supports_force/children_area) * (child->polygon->area() * pow(SCALING_FACTOR, 2)); above->supports_force += below.supports_force * above->area / above_area;
} }
// Now iterate over all polygons and append new points if needed. // Now iterate over all polygons and append new points if needed.
for (Structure& s : m_structures_new) { for (Structure& s : structures_new) {
if (s.structures_below.empty()) {// completely new island - needs support no doubt if (s.structures_below.empty()) // completely new island - needs support no doubt
uniformly_cover(*s.polygon, s, true); uniformly_cover(*s.polygon, s, true);
} else
else {
// Let's see if there's anything that overlaps enough to need supports: // 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. // 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); uniformly_cover(p, s);
}
} }
// We should also check if current support is enough given the polygon area. // We should also check if current support is enough given the polygon area.
for (Structure& s : m_structures_new) { for (Structure& s : structures_new) {
ExPolygons e; // Areas not supported by the areas below.
float e_area = 0.f; ExPolygons e = diff_ex(to_polygons(*s.polygon), s.polygons_below());
for (const Structure* a : s.structures_below) { float e_area = 0.f;
e.push_back(*a->polygon); for (const ExPolygon &ex : e)
e_area += e.back().area() * SCALING_FACTOR * SCALING_FACTOR; e_area += float(ex.area());
}
e = diff_ex(ExPolygons{*s.polygon}, e);
// Penalization resulting from large diff from the last layer: // 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(); }); 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 (!e.empty())
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
uniformly_cover(*largest_it, s); uniformly_cover(*largest_it, s);
} }
} }
// All is done. Prepare to advance to the next layer. // All is done. Prepare to advance to the next layer.
m_structures_old = m_structures_new; structures_old = std::move(structures_new);
m_structures_new.clear(); structures_new.clear();
m_throw_on_cancel(); m_throw_on_cancel();
@ -195,15 +196,6 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& 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) 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)); //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. // We will cover the island another way.
// For now we'll just place the points randomly not too close to the others. // 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::random_device rd;
std::mt19937 gen(rd()); std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0., 1.); std::uniform_real_distribution<> dis(0., 1.);
std::vector<Vec3d> island_new_points; std::vector<Vec3d> island_new_points;
const BoundingBox& bb = get_extents(island); const BoundingBox bb = get_extents(island);
const int refused_limit = 30 * ((float)bb.size()(0)*bb.size()(1) / (float)island.area()); const int refused_limit = (int)floor(30.f * ((float)bb.size()(0)*bb.size()(1) / (float)island.area()) + 0.5f);
int refused_points = 0; 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) { while (refused_points < refused_limit) {
Point out; Point out;
if (refused_points == 0 && island_new_points.empty()) // first iteration if (refused_points == 0 && island_new_points.empty()) // first iteration
@ -227,15 +223,17 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct
else else
out = Point(bb.min(0) + bb.size()(0) * dis(gen), bb.min(1) + bb.size()(1) * dis(gen)); 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; bool add_it = true;
if (!island.contour.contains(out)) if (!island.contour.contains(out))
add_it = false; add_it = false;
else else
for (const Polygon& hole : island.holes) for (const Polygon& hole : island.holes)
if (hole.contains(out)) if (hole.contains(out)) {
add_it = false; add_it = false;
break;
}
if (add_it) { if (add_it) {
for (const Vec3d& p : island_new_points) { for (const Vec3d& p : island_new_points) {
@ -254,14 +252,15 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct
++refused_points; ++refused_points;
} }
for (const Vec3d& p : island_new_points) for (const Vec3d& p : island_new_points) {
add_point(Point(scale_(p.x()), scale_(p.y())), structure, is_new_island); 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 #ifdef SLA_AUTOSUPPORTS_DEBUG
void SLAAutoSupports::output_structures() const void SLAAutoSupports::output_structures(const std::vector<Structure>& structures)
{ {
const std::vector<Structure>& structures = m_structures_new;
for (unsigned int i=0 ; i<structures.size(); ++i) { for (unsigned int i=0 ; i<structures.size(); ++i) {
std::stringstream ss; std::stringstream ss;
ss << structures[i].unique_id.count() << "_" << std::setw(10) << std::setfill('0') << 1000 + (int)structures[i].height/1000 << ".png"; ss << structures[i].unique_id.count() << "_" << std::setw(10) << std::setfill('0') << 1000 + (int)structures[i].height/1000 << ".png";
@ -269,7 +268,7 @@ void SLAAutoSupports::output_structures() const
} }
} }
void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string filename) const void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, const std::string &filename)
{ {
BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000)); BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000));
Slic3r::SVG svg_cummulative(filename, bb); Slic3r::SVG svg_cummulative(filename, bb);

View file

@ -16,8 +16,8 @@ public:
float density_at_45; float density_at_45;
float minimal_z; float minimal_z;
/////////////// ///////////////
float support_force = 30; // a force one point can support (arbitrary force unit) float support_force = 30.f; // a force one point can support (arbitrary force unit)
float tear_pressure = 1; // pressure that the display exerts (the force unit per mm2) 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<ExPolygons>& slices, SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices,
@ -27,32 +27,65 @@ public:
private: private:
std::vector<sla::SupportPoint> m_output; std::vector<sla::SupportPoint> m_output;
#ifdef SLA_AUTOSUPPORTS_DEBUG
void output_expolygons(const ExPolygons& expolys, std::string filename) const;
void output_structures() const;
#endif // SLA_AUTOSUPPORTS_DEBUG
SLAAutoSupports::Config m_config; SLAAutoSupports::Config m_config;
struct Structure { struct Structure {
Structure(const ExPolygon& poly, float h) : height(h), unique_id(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())) { Structure(const ExPolygon& poly, const BoundingBox &bbox, const Vec2f &centroid, float area, float h) : polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h)
polygon = &poly; #ifdef SLA_AUTOSUPPORTS_DEBUG
} , unique_id(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()))
#endif /* SLA_AUTOSUPPORTS_DEBUG */
{}
const ExPolygon* polygon = nullptr; const ExPolygon* polygon = nullptr;
const BoundingBox bbox;
const Vec2f centroid = Vec3f::Zero();
const float area = 0.f;
std::vector<const Structure*> structures_below; std::vector<const Structure*> structures_below;
float height = 0; 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 = 0.f;
#ifdef SLA_AUTOSUPPORTS_DEBUG
std::chrono::milliseconds unique_id; 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<Structure> m_structures_old;
std::vector<Structure> m_structures_new;
float m_supports_force_total = 0.f; float m_supports_force_total = 0.f;
void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights); void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights);
void add_point(const Point& point, Structure& structure, bool island = false);
void uniformly_cover(const ExPolygon& island, Structure& structure, bool is_new_island = false, bool just_one = false); void uniformly_cover(const ExPolygon& island, Structure& structure, bool is_new_island = false, bool just_one = false);
void project_onto_mesh(std::vector<sla::SupportPoint>& points) const; void project_onto_mesh(std::vector<sla::SupportPoint>& points) const;
#ifdef SLA_AUTOSUPPORTS_DEBUG
static void output_expolygons(const ExPolygons& expolys, const std::string &filename);
static void output_structures(const std::vector<Structure> &structures);
#endif // SLA_AUTOSUPPORTS_DEBUG
std::function<void(void)> m_throw_on_cancel; std::function<void(void)> m_throw_on_cancel;
const sla::EigenMesh3D& m_emesh; const sla::EigenMesh3D& m_emesh;

View file

@ -28,7 +28,7 @@ struct SupportPoint {
pos(position), head_front_radius(head_radius), is_new_island(new_island) {} pos(position), head_front_radius(head_radius), is_new_island(new_island) {}
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data) : SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data) :
pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4)) {} 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 (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
}; };