2019-08-16 16:17:37 +02:00
//#include "igl/random_points_on_mesh.h"
//#include "igl/AABB.h"
2018-12-07 14:10:16 +01:00
2019-02-06 10:57:45 +01:00
#include <tbb/parallel_for.h>
2018-12-07 14:10:16 +01:00
#include "SLAAutoSupports.hpp"
#include "Model.hpp"
2018-12-14 16:15:59 +01:00
#include "ExPolygon.hpp"
#include "SVG.hpp"
#include "Point.hpp"
2018-12-21 12:57:14 +01:00
#include "ClipperUtils.hpp"
2019-02-17 13:05:22 +01:00
#include "Tesselate.hpp"
#include "libslic3r.h"
2018-12-07 14:10:16 +01:00
#include <iostream>
2018-12-21 12:35:20 +01:00
#include <random>
2018-12-07 14:10:16 +01:00
namespace Slic3r {
2018-12-21 12:35:20 +01:00
2019-02-04 08:40:20 +01:00
/*float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2)
2018-12-07 14:10:16 +01:00
{
n1.normalize();
n2.normalize();
2018-12-21 12:35:20 +01:00
Vec3d v = (p2-p1);
2018-12-07 14:10:16 +01:00
v.normalize();
float c1 = n1.dot(v);
float c2 = n2.dot(v);
float result = pow(p1(0)-p2(0), 2) + pow(p1(1)-p2(1), 2) + pow(p1(2)-p2(2), 2);
// Check for division by zero:
if(fabs(c1 - c2) > 0.0001)
result *= (asin(c1) - asin(c2)) / (c1 - c2);
return result;
}
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
// to get the user-set density for 45 deg. So it ends up as density_0 * cos(K * angle).
2018-12-07 14:53:24 +01:00
float K = 4.f * float(acos(m_config.density_at_45/m_config.density_at_horizontal) / M_PI);
return std::max(0.f, float(m_config.density_at_horizontal * cos(K*angle)));
2018-12-07 14:10:16 +01:00
}
2018-12-21 12:35:20 +01:00
float SLAAutoSupports::distance_limit(float angle) const
{
return 1./(2.4*get_required_density(angle));
2019-01-30 08:26:23 +01:00
}*/
SLAAutoSupports :: SLAAutoSupports ( const TriangleMesh & mesh , const sla :: EigenMesh3D & emesh , const std :: vector < ExPolygons >& slices , const std :: vector < float >& heights ,
2019-04-02 10:54:14 +02:00
const Config & config , std :: function < void ( void ) > throw_on_cancel , std :: function < void ( int ) > statusfn )
: m_config ( config ), m_emesh ( emesh ), m_throw_on_cancel ( throw_on_cancel ), m_statusfn ( statusfn )
2019-01-30 08:26:23 +01:00
{
process ( slices , heights );
project_onto_mesh ( m_output );
}
void SLAAutoSupports :: project_onto_mesh ( std :: vector < sla :: SupportPoint >& points ) const
{
// The function makes sure that all the points are really exactly placed on the mesh.
2019-02-04 09:50:25 +01:00
2019-02-06 10:57:45 +01:00
// Use a reasonable granularity to account for the worker thread synchronization cost.
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( 0 , points . size (), 64 ),
[ this , & points ]( const tbb :: blocked_range < size_t >& 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<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);
2019-02-04 09:50:25 +01:00
2019-02-06 10:57:45 +01:00
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. ));
2019-02-04 09:50:25 +01:00
2019-02-06 10:57:45 +01:00
bool up = hit_up . face () != - 1 ;
bool down = hit_down . face () != - 1 ;
2019-01-30 08:26:23 +01:00
2019-02-06 10:57:45 +01:00
if ( ! up && ! down )
continue ;
2019-01-30 08:26:23 +01:00
2019-02-06 10:57:45 +01:00
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<float>();
2019-02-04 09:50:25 +01:00
2019-02-06 10:57:45 +01:00
p = p + ( hit . distance () * hit . direction ()). cast < float > ();
}
});
2019-01-30 08:26:23 +01:00
}
2019-02-18 11:46:06 +01:00
static std :: vector < SLAAutoSupports :: MyLayer > make_layers (
const std :: vector < ExPolygons >& slices , const std :: vector < float >& heights ,
std :: function < void ( void ) > throw_on_cancel )
{
assert ( slices . size () == heights . size ());
// Allocate empty layers.
std :: vector < SLAAutoSupports :: MyLayer > layers ;
layers . reserve ( slices . size ());
for ( size_t i = 0 ; i < slices . size (); ++ i )
2019-08-16 16:17:37 +02:00
layers . emplace_back ( i , heights [ i ]);
2019-02-18 11:46:06 +01:00
// 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 ( 0.047f , 2.f );
// Use a reasonable granularity to account for the worker thread synchronization cost.
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( 0 , layers . size (), 32 ),
[ & layers , & slices , & heights , pixel_area , throw_on_cancel ]( const tbb :: blocked_range < size_t >& 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 ();
2019-08-16 16:17:37 +02:00
SLAAutoSupports :: MyLayer & layer = layers [ layer_id ];
2019-02-18 11:46:06 +01:00
const ExPolygons & islands = slices [ layer_id ];
//FIXME WTF?
const float height = ( layer_id > 2 ? heights [ layer_id - 3 ] : heights [ 0 ] - ( heights [ 1 ] - heights [ 0 ]));
2019-08-16 16:17:37 +02:00
layer . islands . reserve ( islands . size ());
2019-02-18 11:46:06 +01:00
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.
2019-08-16 16:17:37 +02:00
layer . islands . emplace_back ( layer , island , get_extents ( island . contour ), Slic3r :: unscale ( island . contour . centroid ()). cast < float > (), area , height );
2019-02-18 11:46:06 +01:00
}
}
});
2019-08-16 16:17:37 +02:00
// Calculate overlap of successive layers. Link overlapping islands.
tbb :: parallel_for ( tbb :: blocked_range < size_t > ( 1 , layers . size (), 8 ),
[ & layers , & heights , throw_on_cancel ]( const tbb :: blocked_range < size_t >& 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 ];
2019-02-18 11:46:06 +01:00
//FIXME WTF?
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 )));
2019-02-27 08:49:09 +01:00
const float slope_angle = 75.f * ( float ( M_PI ) / 180.f ); // smaller number - less supports
const float slope_offset = float ( scale_ ( layer_height / std :: tan ( slope_angle )));
2019-08-16 16:17:37 +02:00
//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 ) {
2019-02-20 10:46:49 +01:00
float overlap_area = top . overlap_area ( bottom );
if ( overlap_area > 0 ) {
2019-08-16 16:17:37 +02:00
top . islands_below . emplace_back ( & bottom , overlap_area );
2019-02-20 10:46:49 +01:00
bottom . islands_above . emplace_back ( & top , overlap_area );
2019-02-18 11:46:06 +01:00
}
2019-02-20 10:46:49 +01:00
}
2019-02-18 11:46:06 +01:00
if ( ! top . islands_below . empty ()) {
Polygons top_polygons = to_polygons ( * top . polygon );
2019-08-16 16:17:37 +02:00
Polygons bottom_polygons = top . polygons_below ();
2019-02-18 11:46:06 +01:00
top . overhangs = diff_ex ( top_polygons , bottom_polygons );
if ( ! top . overhangs . empty ()) {
top . overhangs_area = 0.f ;
std :: vector < std :: pair < ExPolygon * , float >> expolys_with_areas ;
for ( ExPolygon & ex : top . overhangs ) {
float area = float ( ex . area ());
expolys_with_areas . emplace_back ( & ex , area );
top . overhangs_area += area ;
}
2019-08-16 16:17:37 +02:00
std :: sort ( expolys_with_areas . begin (), expolys_with_areas . end (),
2019-02-18 11:46:06 +01:00
[]( const std :: pair < ExPolygon * , float > & p1 , const std :: pair < ExPolygon * , float > & p2 )
{ return p1 . second > p2 . second ; });
ExPolygons overhangs_sorted ;
for ( auto & p : expolys_with_areas )
overhangs_sorted . emplace_back ( std :: move ( * p . first ));
2019-08-16 16:17:37 +02:00
top . overhangs = std :: move ( overhangs_sorted );
2019-02-18 11:46:06 +01:00
top . overhangs_area *= float ( SCALING_FACTOR * SCALING_FACTOR );
2019-02-27 08:49:09 +01:00
top . overhangs_slopes = diff_ex ( top_polygons , offset ( bottom_polygons , slope_offset ));
2019-02-18 11:46:06 +01:00
top . dangling_areas = diff_ex ( top_polygons , offset ( bottom_polygons , between_layers_offset ));
}
}
2019-08-16 16:17:37 +02:00
}
}
});
2019-02-18 11:46:06 +01:00
return layers ;
}
2019-01-30 08:26:23 +01:00
void SLAAutoSupports :: process ( const std :: vector < ExPolygons >& slices , const std :: vector < float >& heights )
{
2019-02-18 11:46:06 +01:00
#ifdef SLA_AUTOSUPPORTS_DEBUG
2019-01-30 08:26:23 +01:00
std :: vector < std :: pair < ExPolygon , coord_t >> islands ;
2019-02-18 11:46:06 +01:00
#endif /* SLA_AUTOSUPPORTS_DEBUG */
2019-01-30 08:26:23 +01:00
2019-02-18 11:46:06 +01:00
std :: vector < SLAAutoSupports :: MyLayer > layers = make_layers ( slices , heights , m_throw_on_cancel );
2019-01-30 08:26:23 +01:00
2019-02-18 11:46:06 +01:00
PointGrid3D point_grid ;
point_grid . cell_size = Vec3f ( 10.f , 10.f , 10.f );
2019-02-01 16:12:00 +01:00
2019-04-02 10:54:14 +02:00
double increment = 100.0 / layers . size ();
double status = 0 ;
2019-02-18 11:46:06 +01:00
for ( unsigned int layer_id = 0 ; layer_id < layers . size (); ++ layer_id ) {
2019-02-19 10:09:41 +01:00
SLAAutoSupports :: MyLayer * layer_top = & layers [ layer_id ];
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 )
2019-08-16 16:17:37 +02:00
for ( Structure :: Link & bottom_link : top . islands_below ) {
2019-02-20 10:46:49 +01:00
Structure & bottom = * bottom_link . island ;
2019-06-25 13:06:04 +02:00
//float centroids_dist = (bottom.centroid - top.centroid).norm();
2019-02-18 11:46:06 +01:00
// Penalization resulting from centroid offset:
2019-02-17 13:05:22 +01:00
// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area));
2019-02-20 10:46:49 +01:00
float & support_force = support_force_bottom [ & bottom - layer_bottom -> islands . data ()];
2019-02-19 10:09:41 +01:00
//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.
2019-08-16 16:17:37 +02:00
// One should rather work with the overlap area vs overhang area.
2019-02-20 10:46:49 +01:00
// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom.area));
2019-02-18 11:46:06 +01:00
// Penalization resulting from increasing polygon area:
2019-02-20 10:46:49 +01:00
support_force *= std :: min ( 1.f , 20.f * bottom . area / top . area );
2019-02-18 11:46:06 +01:00
}
2019-01-30 08:26:23 +01:00
// Let's assign proper support force to each of them:
2019-02-18 11:46:06 +01:00
if ( layer_id > 0 ) {
2019-02-19 10:09:41 +01:00
for ( Structure & below : layer_bottom -> islands ) {
float below_support_force = support_force_bottom [ & below - layer_bottom -> islands . data ()];
2019-02-20 10:46:49 +01:00
float above_overlap_area = 0.f ;
2019-08-16 16:17:37 +02:00
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 ;
2019-02-18 11:46:06 +01:00
}
2019-01-30 08:26:23 +01:00
}
// Now iterate over all polygons and append new points if needed.
2019-02-19 10:09:41 +01:00
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 );
2019-06-25 13:06:04 +02:00
//float force_deficit = s.support_force_deficit(m_config.tear_pressure());
2019-02-20 10:46:49 +01:00
if ( s . islands_below . empty ()) { // completely new island - needs support no doubt
2019-02-19 10:09:41 +01:00
uniformly_cover ({ * s . polygon }, s , point_grid , true );
2019-02-20 10:46:49 +01:00
} else if ( ! s . dangling_areas . empty ()) {
2019-01-30 08:26:23 +01:00
// 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.
2019-02-19 10:09:41 +01:00
//FIXME is it an island point or not? Vojtech thinks it is.
uniformly_cover ( s . dangling_areas , s , point_grid );
2019-02-27 08:49:09 +01:00
} else if ( ! s . overhangs_slopes . empty ()) {
2019-02-19 10:09:41 +01:00
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
2019-02-27 08:49:09 +01:00
uniformly_cover ( s . overhangs_slopes , s , point_grid );
2019-01-30 08:26:23 +01:00
}
}
m_throw_on_cancel ();
2019-04-02 10:54:14 +02:00
status += increment ;
m_statusfn ( int ( std :: round ( status )));
2019-01-30 08:26:23 +01:00
#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 */
}
}
2019-02-17 13:05:22 +01:00
std :: vector < Vec2f > sample_expolygon ( const ExPolygon & expoly , float samples_per_mm2 , std :: mt19937 & rng )
2019-08-16 16:17:37 +02:00
{
2019-02-17 13:05:22 +01:00
// Triangulate the polygon with holes into triplets of 3D points.
std :: vector < Vec2f > triangles = Slic3r :: triangulate_expolygon_2f ( expoly );
std :: vector < Vec2f > out ;
if ( ! triangles . empty ())
{
// Calculate area of each triangle.
std :: vector < float > 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 < size_t > ( 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 < Vec2f > sample_expolygon_with_boundary ( const ExPolygon & expoly , float samples_per_mm2 , float samples_per_mm_boundary , std :: mt19937 & rng )
{
std :: vector < Vec2f > 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 < float > ( pts [ i ]. x ()), unscale < float > ( pts [ i ]. y ()));
}
return out ;
}
2019-02-19 10:09:41 +01:00
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 ;
}
2019-02-18 11:46:06 +01:00
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 )
2019-02-17 13:05:22 +01:00
{
2019-02-18 12:47:15 +01:00
Vec2f corner_min ( std :: numeric_limits < float >:: max (), std :: numeric_limits < float >:: max ());
2019-02-17 13:05:22 +01:00
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 < RawSample > raw_samples_sorted ;
RawSample sample ;
for ( const Vec2f & pt : raw_samples ) {
sample . coord = pt ;
sample . cell_id = (( pt - corner_min ) / radius ). cast < int > ();
raw_samples_sorted . emplace_back ( sample );
}
2019-08-16 16:17:37 +02:00
std :: sort ( raw_samples_sorted . begin (), raw_samples_sorted . end (), []( const RawSample & lhs , const RawSample & rhs )
2019-02-17 13:05:22 +01:00
{ 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 {
2019-02-18 12:47:15 +01:00
std :: size_t operator ()( const Vec2i & cell_id ) const {
2019-02-17 13:05:22 +01:00
return std :: hash < int > ()( cell_id . x ()) ^ std :: hash < int > ()( 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 < Vec2i , PoissonDiskGridEntry , CellIDHash > Cells ;
2019-02-18 12:47:15 +01:00
Cells cells ;
2019-02-17 13:05:22 +01:00
{
2019-02-18 12:47:15 +01:00
typename Cells :: iterator last_cell_id_it ;
2019-02-17 13:05:22 +01:00
Vec2i last_cell_id ( - 1 , - 1 );
2019-06-25 13:06:04 +02:00
for ( size_t i = 0 ; i < raw_samples_sorted . size (); ++ i ) {
2019-02-17 13:05:22 +01:00
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 ;
2019-07-18 16:32:04 +02:00
data . first_sample_idx = int ( i );
2019-02-17 13:05:22 +01:00
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.
2019-02-18 11:46:06 +01:00
bool conflict = refuse_function ( candidate . coord );
2019-02-17 13:05:22 +01:00
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 < Vec2f > out ;
2019-02-18 12:47:15 +01:00
for ( const auto & it : cells )
2019-02-17 13:05:22 +01:00
for ( int i = 0 ; i < it . second . num_poisson_samples ; ++ i )
out . emplace_back ( it . second . poisson_samples [ i ]);
return out ;
}
2019-02-19 10:09:41 +01:00
void SLAAutoSupports :: uniformly_cover ( const ExPolygons & islands , Structure & structure , PointGrid3D & grid3d , bool is_new_island , bool just_one )
2019-01-30 08:26:23 +01:00
{
//int num_of_points = std::max(1, (int)((island.area()*pow(SCALING_FACTOR, 2) * m_config.tear_pressure)/m_config.support_force));
2019-02-17 13:05:22 +01:00
2019-02-19 16:34:52 +01:00
const float support_force_deficit = structure . support_force_deficit ( m_config . tear_pressure ());
2019-02-19 10:09:41 +01:00
if ( support_force_deficit < 0 )
return ;
// Number of newly added points.
2019-02-19 16:34:52 +01:00
const size_t poisson_samples_target = size_t ( ceil ( support_force_deficit / m_config . support_force ()));
2019-02-19 10:09:41 +01:00
2019-02-19 16:34:52 +01:00
const float density_horizontal = m_config . tear_pressure () / m_config . support_force ();
2019-02-17 13:05:22 +01:00
//FIXME why?
2019-02-19 16:34:52 +01:00
float poisson_radius = std :: max ( m_config . minimal_distance , 1.f / ( 5.f * density_horizontal ));
2019-02-17 13:05:22 +01:00
// const float poisson_radius = 1.f / (15.f * density_horizontal);
2019-02-19 10:09:41 +01:00
const float samples_per_mm2 = 30.f / ( float ( M_PI ) * poisson_radius * poisson_radius );
2019-02-18 11:46:06 +01:00
// Minimum distance between samples, in 3D space.
2019-02-19 10:09:41 +01:00
// float min_spacing = poisson_radius / 3.f;
float min_spacing = poisson_radius ;
2019-01-30 08:26:23 +01:00
2019-02-06 10:57:45 +01:00
//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.
2019-02-17 13:05:22 +01:00
std :: random_device rd ;
std :: mt19937 rng ( rd ());
2019-08-16 16:17:37 +02:00
std :: vector < Vec2f > raw_samples = sample_expolygon_with_boundary ( islands , samples_per_mm2 , 5.f / poisson_radius , rng );
2019-02-19 10:09:41 +01:00
std :: vector < Vec2f > poisson_samples ;
for ( size_t iter = 0 ; iter < 4 ; ++ iter ) {
2019-08-16 16:17:37 +02:00
poisson_samples = poisson_disk_from_samples ( raw_samples , poisson_radius ,
2019-02-19 10:09:41 +01:00
[ & structure , & grid3d , min_spacing ]( const Vec2f & pos ) {
return grid3d . collides_with ( pos , & structure , min_spacing );
});
2019-02-19 16:34:52 +01:00
if ( poisson_samples . size () >= poisson_samples_target || m_config . minimal_distance > poisson_radius - EPSILON )
2019-02-19 10:09:41 +01:00
break ;
float coeff = 0.5f ;
if ( poisson_samples . size () * 2 > poisson_samples_target )
coeff = float ( poisson_samples . size ()) / float ( poisson_samples_target );
2019-02-19 16:34:52 +01:00
poisson_radius = std :: max ( m_config . minimal_distance , poisson_radius * coeff );
min_spacing = std :: max ( m_config . minimal_distance , min_spacing * coeff );
2019-02-19 10:09:41 +01:00
}
2019-01-30 08:26:23 +01:00
2019-02-17 13:05:22 +01:00
#ifdef SLA_AUTOSUPPORTS_DEBUG
2019-08-16 16:17:37 +02:00
{
static int irun = 0 ;
Slic3r :: SVG svg ( debug_out_path ( "SLA_supports-uniformly_cover-%d.svg" , irun ++ ), get_extents ( islands ));
2019-02-19 10:09:41 +01:00
for ( const ExPolygon & island : islands )
svg . draw ( island );
2019-08-16 16:17:37 +02:00
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" );
}
2019-02-17 13:05:22 +01:00
#endif /* NDEBUG */
2019-01-30 08:26:23 +01:00
2019-02-18 11:46:06 +01:00
// assert(! poisson_samples.empty());
2019-02-19 10:09:41 +01:00
if ( poisson_samples_target < poisson_samples . size ()) {
2019-08-16 16:17:37 +02:00
std :: shuffle ( poisson_samples . begin (), poisson_samples . end (), rng );
2019-02-19 10:09:41 +01:00
poisson_samples . erase ( poisson_samples . begin () + poisson_samples_target , poisson_samples . end ());
}
2019-02-17 13:05:22 +01:00
for ( const Vec2f & pt : poisson_samples ) {
2019-03-13 12:02:33 +01:00
m_output . emplace_back ( float ( pt ( 0 )), float ( pt ( 1 )), structure . height , m_config . head_diameter / 2.f , is_new_island );
2019-02-19 16:34:52 +01:00
structure . supports_force_this_layer += m_config . support_force ();
2019-02-18 11:46:06 +01:00
grid3d . insert ( pt , & structure );
2019-02-06 10:57:45 +01:00
}
2018-12-21 12:35:20 +01:00
}
2018-12-07 14:10:16 +01:00
2018-12-22 10:55:15 +01:00
#ifdef SLA_AUTOSUPPORTS_DEBUG
2019-02-06 10:57:45 +01:00
void SLAAutoSupports :: output_structures ( const std :: vector < Structure >& structures )
2019-01-30 08:26:23 +01:00
{
for ( unsigned int i = 0 ; i < structures . size (); ++ i ) {
std :: stringstream ss ;
ss << structures [ i ]. unique_id . count () << "_" << std :: setw ( 10 ) << std :: setfill ( '0' ) << 1000 + ( int ) structures [ i ]. height / 1000 << ".png" ;
output_expolygons ( std :: vector < ExPolygon > { * structures [ i ]. polygon }, ss . str ());
}
}
2019-02-06 10:57:45 +01:00
void SLAAutoSupports :: output_expolygons ( const ExPolygons & expolys , const std :: string & filename )
2018-12-14 16:15:59 +01:00
{
BoundingBox bb ( Point ( - 30000000 , - 30000000 ), Point ( 30000000 , 30000000 ));
Slic3r :: SVG svg_cummulative ( filename , bb );
for ( size_t i = 0 ; i < expolys . size (); ++ i ) {
/*Slic3r::SVG svg("single"+std::to_string(i)+".svg", bb);
svg.draw(expolys[i]);
svg.draw_outline(expolys[i].contour, "black", scale_(0.05));
svg.draw_outline(expolys[i].holes, "blue", scale_(0.05));
svg.Close();*/
svg_cummulative . draw ( expolys [ i ]);
svg_cummulative . draw_outline ( expolys [ i ]. contour , "black" , scale_ ( 0.05 ));
svg_cummulative . draw_outline ( expolys [ i ]. holes , "blue" , scale_ ( 0.05 ));
}
}
2019-01-30 08:26:23 +01:00
#endif
2018-12-21 12:35:20 +01:00
2019-01-14 17:28:02 +01:00
} // namespace Slic3r