mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 08:47:52 -06:00
SLA auto supports: Work with support force deficit to sprinkle support points.
Use OpenGL emissive material to render support points.
This commit is contained in:
parent
cb4763bb32
commit
8b4bd7177b
4 changed files with 98 additions and 40 deletions
|
@ -193,49 +193,54 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
|
||||||
point_grid.cell_size = Vec3f(10.f, 10.f, 10.f);
|
point_grid.cell_size = Vec3f(10.f, 10.f, 10.f);
|
||||||
|
|
||||||
for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) {
|
for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) {
|
||||||
SLAAutoSupports::MyLayer &layer_top = layers[layer_id];
|
SLAAutoSupports::MyLayer *layer_top = &layers[layer_id];
|
||||||
for (Structure &top : layer_top.islands)
|
SLAAutoSupports::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr;
|
||||||
|
std::vector<float> 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) {
|
for (Structure *bottom : top.islands_below) {
|
||||||
float centroids_dist = (bottom->centroid - top.centroid).norm();
|
float centroids_dist = (bottom->centroid - top.centroid).norm();
|
||||||
// Penalization resulting from centroid offset:
|
// 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, 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:
|
// 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:
|
// Let's assign proper support force to each of them:
|
||||||
if (layer_id > 0) {
|
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;
|
float above_area = 0.f;
|
||||||
for (Structure *above : below.islands_above)
|
for (Structure *above : below.islands_above)
|
||||||
above_area += above->area;
|
above_area += above->area;
|
||||||
for (Structure *above : below.islands_above)
|
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.
|
// 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
|
if (s.islands_below.empty()) // completely new island - needs support no doubt
|
||||||
uniformly_cover(*s.polygon, s, point_grid, true);
|
uniformly_cover({ *s.polygon }, s, point_grid, true);
|
||||||
else
|
else if (! s.dangling_areas.empty()) {
|
||||||
// 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:
|
||||||
// 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 : s.dangling_areas)
|
|
||||||
//FIXME is it an island point or not? Vojtech thinks it is.
|
//FIXME is it an island point or not? Vojtech thinks it is.
|
||||||
uniformly_cover(p, s, point_grid);
|
uniformly_cover(s.dangling_areas, s, point_grid);
|
||||||
}
|
} else if (! s.overhangs.empty()) {
|
||||||
|
|
||||||
// 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.
|
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
|
||||||
uniformly_cover(s.overhangs.front(), s, point_grid);
|
uniformly_cover(s.overhangs, s, point_grid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +309,14 @@ std::vector<Vec2f> sample_expolygon_with_boundary(const ExPolygon &expoly, float
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Vec2f> sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng)
|
||||||
|
{
|
||||||
|
std::vector<Vec2f> out;
|
||||||
|
for (const ExPolygon &expoly : expolys)
|
||||||
|
append(out, sample_expolygon_with_boundary(expoly, samples_per_mm2, samples_per_mm_boundary, rng));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename REFUSE_FUNCTION>
|
template<typename REFUSE_FUNCTION>
|
||||||
static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec2f> &raw_samples, float radius, REFUSE_FUNCTION refuse_function)
|
static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec2f> &raw_samples, float radius, REFUSE_FUNCTION refuse_function)
|
||||||
{
|
{
|
||||||
|
@ -419,31 +432,50 @@ static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& structure, PointGrid3D &grid3d, bool is_new_island, bool just_one)
|
void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, 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));
|
//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);
|
||||||
|
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 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?
|
//FIXME why?
|
||||||
const float poisson_radius = 1.f / (5.f * density_horizontal);
|
float poisson_radius = 1.f / (5.f * density_horizontal);
|
||||||
// const float poisson_radius = 1.f / (15.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);
|
const float samples_per_mm2 = 30.f / (float(M_PI) * poisson_radius * poisson_radius);
|
||||||
// Minimum distance between samples, in 3D space.
|
// Minimum distance between samples, in 3D space.
|
||||||
const float min_spacing = poisson_radius / 3.f;
|
// float min_spacing = poisson_radius / 3.f;
|
||||||
|
float min_spacing = poisson_radius;
|
||||||
|
|
||||||
//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.
|
//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 rng(rd());
|
std::mt19937 rng(rd());
|
||||||
std::vector<Vec2f> raw_samples = sample_expolygon_with_boundary(island, samples_per_mm2, 5.f / poisson_radius, rng);
|
std::vector<Vec2f> raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng);
|
||||||
std::vector<Vec2f> poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius,
|
std::vector<Vec2f> 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) {
|
[&structure, &grid3d, min_spacing](const Vec2f &pos) {
|
||||||
return grid3d.collides_with(pos, &structure, min_spacing);
|
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
|
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||||
{
|
{
|
||||||
static int irun = 0;
|
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(island));
|
||||||
|
for (const ExPolygon &island : islands)
|
||||||
svg.draw(island);
|
svg.draw(island);
|
||||||
for (const Vec2f &pt : raw_samples)
|
for (const Vec2f &pt : raw_samples)
|
||||||
svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red");
|
svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red");
|
||||||
|
@ -453,9 +485,13 @@ void SLAAutoSupports::uniformly_cover(const ExPolygon& island, Structure& struct
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
// assert(! poisson_samples.empty());
|
// 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) {
|
for (const Vec2f &pt : poisson_samples) {
|
||||||
m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island);
|
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);
|
grid3d.insert(pt, &structure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ public:
|
||||||
float density_at_45;
|
float density_at_45;
|
||||||
float minimal_z;
|
float minimal_z;
|
||||||
///////////////
|
///////////////
|
||||||
float support_force = 30.f; // a force one point can support (arbitrary force unit)
|
// 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)
|
float tear_pressure = 1.f; // pressure that the display exerts (the force unit per mm2)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,13 +44,22 @@ public:
|
||||||
float height = 0;
|
float height = 0;
|
||||||
// How well is this ExPolygon held to the print base?
|
// How well is this ExPolygon held to the print base?
|
||||||
// Positive number, the higher the better.
|
// 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
|
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||||
std::chrono::milliseconds unique_id;
|
std::chrono::milliseconds unique_id;
|
||||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
// In release mode, use the optimized container.
|
||||||
boost::container::small_vector<Structure*, 4> islands_above;
|
boost::container::small_vector<Structure*, 4> islands_above;
|
||||||
boost::container::small_vector<Structure*, 4> islands_below;
|
boost::container::small_vector<Structure*, 4> islands_below;
|
||||||
|
#else
|
||||||
|
// In debug mode, use the standard vector, which is well handled by debugger visualizer.
|
||||||
|
std::vector<Structure*> islands_above;
|
||||||
|
std::vector<Structure*> islands_below;
|
||||||
|
#endif
|
||||||
ExPolygons dangling_areas;
|
ExPolygons dangling_areas;
|
||||||
ExPolygons overhangs;
|
ExPolygons overhangs;
|
||||||
float overhangs_area;
|
float overhangs_area;
|
||||||
|
@ -80,6 +90,8 @@ public:
|
||||||
out.emplace_back(*below->polygon);
|
out.emplace_back(*below->polygon);
|
||||||
return out;
|
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 {
|
struct MyLayer {
|
||||||
|
@ -155,7 +167,7 @@ private:
|
||||||
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 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<sla::SupportPoint>& points) const;
|
void project_onto_mesh(std::vector<sla::SupportPoint>& points) const;
|
||||||
|
|
||||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||||
|
|
|
@ -1919,6 +1919,8 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::glColor3fv(render_color);
|
::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
|
// Now render the sphere. Inverse matrix of the instance scaling is applied so that the
|
||||||
// sphere does not scale with the object.
|
// sphere does not scale with the object.
|
||||||
|
@ -1929,6 +1931,12 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
|
||||||
::glPopMatrix();
|
::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)
|
if (!picking)
|
||||||
::glDisable(GL_LIGHTING);
|
::glDisable(GL_LIGHTING);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <wx/clipbrd.h>
|
#include <wx/clipbrd.h>
|
||||||
#include <wx/platinfo.h>
|
#include <wx/platinfo.h>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue