2018-02-22 09:56:05 +01:00
#include "../ClipperUtils.hpp"
2019-09-27 09:51:07 +02:00
#include "../ShortestPath.hpp"
2018-02-22 09:56:05 +01:00
#include "../Surface.hpp"
#include <cmath>
#include <algorithm>
#include <iostream>
#include "FillGyroid.hpp"
namespace Slic3r {
2018-04-13 16:43:35 +02:00
static inline double f ( double x , double z_sin , double z_cos , bool vertical , bool flip )
2018-02-22 09:56:05 +01:00
{
2018-04-13 13:46:31 +02:00
if ( vertical ) {
double phase_offset = ( z_cos < 0 ? M_PI : 0 ) + M_PI ;
double a = sin ( x + phase_offset );
2018-02-23 18:32:35 +01:00
double b = - z_cos ;
2018-04-13 13:46:31 +02:00
double res = z_sin * cos ( x + phase_offset + ( flip ? M_PI : 0. ));
2018-02-23 18:32:35 +01:00
double r = sqrt ( sqr ( a ) + sqr ( b ));
2018-04-13 13:46:31 +02:00
return asin ( a / r ) + asin ( res / r ) + M_PI ;
2018-02-23 18:32:35 +01:00
}
2018-04-13 13:46:31 +02:00
else {
double phase_offset = z_sin < 0 ? M_PI : 0. ;
double a = cos ( x + phase_offset );
double b = - z_sin ;
double res = z_cos * sin ( x + phase_offset + ( flip ? 0 : M_PI ));
double r = sqrt ( sqr ( a ) + sqr ( b ));
return ( asin ( a / r ) + asin ( res / r ) + 0.5 * M_PI );
}
}
static inline Polyline make_wave (
2018-08-21 21:05:24 +02:00
const std :: vector < Vec2d >& one_period , double width , double height , double offset , double scaleFactor ,
2019-06-22 18:56:44 +02:00
double z_cos , double z_sin , bool vertical , bool flip )
2018-04-13 13:46:31 +02:00
{
2018-08-21 21:05:24 +02:00
std :: vector < Vec2d > points = one_period ;
2018-08-17 15:53:43 +02:00
double period = points . back ()( 0 );
2019-06-22 18:56:44 +02:00
if ( width != period ) // do not extend if already truncated
{
2020-11-16 13:11:58 +01:00
points . reserve ( one_period . size () * size_t ( floor ( width / period )));
2019-06-22 18:56:44 +02:00
points . pop_back ();
2020-11-16 13:11:58 +01:00
size_t n = points . size ();
2019-06-22 18:56:44 +02:00
do {
2020-11-16 13:11:58 +01:00
points . emplace_back ( points [ points . size () - n ]. x () + period , points [ points . size () - n ]. y ());
2019-06-22 18:56:44 +02:00
} while ( points . back ()( 0 ) < width - EPSILON );
points . emplace_back ( Vec2d ( width , f ( width , z_sin , z_cos , vertical , flip )));
}
2018-04-13 13:46:31 +02:00
// and construct the final polyline to return:
Polyline polyline ;
2019-06-22 15:59:54 +02:00
polyline . points . reserve ( points . size ());
2018-04-13 13:46:31 +02:00
for ( auto & point : points ) {
2018-08-17 15:53:43 +02:00
point ( 1 ) += offset ;
2021-04-22 11:41:26 +02:00
point ( 1 ) = std :: clamp ( double ( point . y ()), 0. , height );
2018-04-13 13:46:31 +02:00
if ( vertical )
2018-08-17 15:53:43 +02:00
std :: swap ( point ( 0 ), point ( 1 ));
2018-08-17 14:14:24 +02:00
polyline . points . emplace_back (( point * scaleFactor ). cast < coord_t > ());
2018-04-13 13:46:31 +02:00
}
2018-02-23 18:32:35 +01:00
return polyline ;
}
2018-02-22 09:56:05 +01:00
2019-06-20 00:27:22 +02:00
static std :: vector < Vec2d > make_one_period ( double width , double scaleFactor , double z_cos , double z_sin , bool vertical , bool flip , double tolerance )
2018-02-23 18:32:35 +01:00
{
2018-08-21 21:05:24 +02:00
std :: vector < Vec2d > points ;
2019-06-20 00:27:22 +02:00
double dx = M_PI_2 ; // exact coordinates on main inflexion lobes
2018-04-13 13:46:31 +02:00
double limit = std :: min ( 2 * M_PI , width );
2020-11-16 13:11:58 +01:00
points . reserve ( coord_t ( ceil ( limit / tolerance / 3 )));
2018-04-13 13:46:31 +02:00
2019-06-22 18:56:44 +02:00
for ( double x = 0. ; x < limit - EPSILON ; x += dx ) {
2019-06-20 00:27:22 +02:00
points . emplace_back ( Vec2d ( x , f ( x , z_sin , z_cos , vertical , flip )));
2018-02-23 18:32:35 +01:00
}
2019-06-22 18:56:44 +02:00
points . emplace_back ( Vec2d ( limit , f ( limit , z_sin , z_cos , vertical , flip )));
2018-04-13 13:46:31 +02:00
2019-06-20 00:27:22 +02:00
// piecewise increase in resolution up to requested tolerance
for (;;)
{
size_t size = points . size ();
for ( unsigned int i = 1 ; i < size ; ++ i ) {
auto & lp = points [ i - 1 ]; // left point
auto & rp = points [ i ]; // right point
double x = lp ( 0 ) + ( rp ( 0 ) - lp ( 0 )) / 2 ;
double y = f ( x , z_sin , z_cos , vertical , flip );
Vec2d ip = { x , y };
if ( std :: abs ( cross2 ( Vec2d ( ip - lp ), Vec2d ( ip - rp ))) > sqr ( tolerance )) {
points . emplace_back ( std :: move ( ip ));
}
}
if ( size == points . size ())
break ;
else
{
// insert new points in order
std :: sort ( points . begin (), points . end (),
[]( const Vec2d & lhs , const Vec2d & rhs ) { return lhs ( 0 ) < rhs ( 0 ); });
2018-04-13 13:46:31 +02:00
}
}
2019-06-20 00:27:22 +02:00
2018-04-13 13:46:31 +02:00
return points ;
2018-02-23 18:32:35 +01:00
}
2018-02-22 09:56:05 +01:00
2018-04-05 10:31:53 +02:00
static Polylines make_gyroid_waves ( double gridZ , double density_adjusted , double line_spacing , double width , double height )
2018-02-23 18:32:35 +01:00
{
2018-04-13 13:46:31 +02:00
const double scaleFactor = scale_ ( line_spacing ) / density_adjusted ;
2019-06-20 00:27:22 +02:00
2019-08-07 21:58:45 +02:00
// tolerance in scaled units. clamp the maximum tolerance as there's
// no processing-speed benefit to do so beyond a certain point
2019-08-08 16:53:26 +02:00
const double tolerance = std :: min ( line_spacing / 2 , FillGyroid :: PatternTolerance ) / unscale < double > ( scaleFactor );
2019-06-20 00:27:22 +02:00
2019-08-08 16:53:26 +02:00
//scale factor for 5% : 8 712 388
// 1z = 10^-6 mm ?
2018-04-13 13:46:31 +02:00
const double z = gridZ / scaleFactor ;
const double z_sin = sin ( z );
const double z_cos = cos ( z );
bool vertical = ( std :: abs ( z_sin ) <= std :: abs ( z_cos ));
double lower_bound = 0. ;
double upper_bound = height ;
bool flip = true ;
if ( vertical ) {
flip = false ;
lower_bound = - M_PI ;
upper_bound = width - M_PI_2 ;
std :: swap ( width , height );
2018-02-22 09:56:05 +01:00
}
2018-04-13 13:46:31 +02:00
2019-06-20 00:27:22 +02:00
std :: vector < Vec2d > one_period_odd = make_one_period ( width , scaleFactor , z_cos , z_sin , vertical , flip , tolerance ); // creates one period of the waves, so it doesn't have to be recalculated all the time
2019-01-23 10:08:42 +01:00
flip = ! flip ; // even polylines are a bit shifted
2019-06-20 00:27:22 +02:00
std :: vector < Vec2d > one_period_even = make_one_period ( width , scaleFactor , z_cos , z_sin , vertical , flip , tolerance );
2018-04-13 13:46:31 +02:00
Polylines result ;
2019-01-23 10:08:42 +01:00
for ( double y0 = lower_bound ; y0 < upper_bound + EPSILON ; y0 += M_PI ) {
// creates odd polylines
2019-06-22 18:56:44 +02:00
result . emplace_back ( make_wave ( one_period_odd , width , height , y0 , scaleFactor , z_cos , z_sin , vertical , flip ));
2019-01-23 10:08:42 +01:00
// creates even polylines
y0 += M_PI ;
if ( y0 < upper_bound + EPSILON ) {
2019-06-22 18:56:44 +02:00
result . emplace_back ( make_wave ( one_period_even , width , height , y0 , scaleFactor , z_cos , z_sin , vertical , flip ));
2019-01-23 10:08:42 +01:00
}
}
2018-04-13 13:46:31 +02:00
2018-02-22 09:56:05 +01:00
return result ;
}
2019-11-12 13:19:17 +01:00
// FIXME: needed to fix build on Mac on buildserver
constexpr double FillGyroid :: PatternTolerance ;
2018-02-22 09:56:05 +01:00
void FillGyroid :: _fill_surface_single (
const FillParams & params ,
unsigned int thickness_layers ,
const std :: pair < float , Point > & direction ,
2020-11-16 13:11:58 +01:00
ExPolygon expolygon ,
2018-02-22 09:56:05 +01:00
Polylines & polylines_out )
{
2020-11-16 13:11:58 +01:00
auto infill_angle = float ( this -> angle + ( CorrectionAngle * 2 * M_PI ) / 360. );
2019-11-19 12:44:55 +01:00
if ( std :: abs ( infill_angle ) >= EPSILON )
2019-08-07 21:45:23 +02:00
expolygon . rotate ( - infill_angle );
2019-07-16 01:15:00 +02:00
2018-02-22 09:56:05 +01:00
BoundingBox bb = expolygon . contour . bounding_box ();
2018-04-05 10:31:53 +02:00
// Density adjusted to have a good %of weight.
2019-08-07 21:58:45 +02:00
double density_adjusted = std :: max ( 0. , params . density * DensityAdjust );
2018-04-05 10:31:53 +02:00
// Distance between the gyroid waves in scaled coordinates.
coord_t distance = coord_t ( scale_ ( this -> spacing ) / density_adjusted );
2018-02-22 09:56:05 +01:00
// align bounding box to a multiple of our grid module
2021-04-08 15:29:40 +02:00
bb . merge ( align_to_grid ( bb . min , Point ( 2 * M_PI * distance , 2 * M_PI * distance )));
2018-04-13 13:46:31 +02:00
2018-02-22 09:56:05 +01:00
// generate pattern
2019-11-14 17:02:32 +01:00
Polylines polylines = make_gyroid_waves (
2018-02-23 18:32:35 +01:00
scale_ ( this -> z ),
2018-04-05 10:31:53 +02:00
density_adjusted ,
2018-02-22 09:56:05 +01:00
this -> spacing ,
2018-08-17 15:53:43 +02:00
ceil ( bb . size ()( 0 ) / distance ) + 1. ,
ceil ( bb . size ()( 1 ) / distance ) + 1. );
2018-02-22 09:56:05 +01:00
2019-11-07 14:03:50 +01:00
// shift the polyline to the grid origin
2019-11-14 17:02:32 +01:00
for ( Polyline & pl : polylines )
2019-11-07 14:03:50 +01:00
pl . translate ( bb . min );
2018-02-22 09:56:05 +01:00
2021-05-03 11:39:53 +02:00
polylines = intersection_pl ( polylines , expolygon );
2019-01-23 10:08:42 +01:00
2020-11-25 14:55:39 +01:00
if ( ! polylines . empty ()) {
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
// The infill perimeter lines should be separated by around a single infill line width.
const double minlength = scale_ ( 0.8 * this -> spacing );
2019-11-14 17:02:32 +01:00
polylines . erase (
2020-11-25 14:55:39 +01:00
std :: remove_if ( polylines . begin (), polylines . end (), [ minlength ]( const Polyline & pl ) { return pl . length () < minlength ; }),
2019-11-14 17:02:32 +01:00
polylines . end ());
2020-11-25 14:55:39 +01:00
}
2019-11-14 17:02:32 +01:00
if ( ! polylines . empty ()) {
// connect lines
size_t polylines_out_first_idx = polylines_out . size ();
2020-11-20 13:36:58 +01:00
if ( params . dont_connect ())
2020-11-16 12:51:51 +01:00
append ( polylines_out , chain_polylines ( polylines ));
2019-11-07 14:03:50 +01:00
else
2019-12-02 15:01:52 +01:00
this -> connect_infill ( std :: move ( polylines ), expolygon , polylines_out , this -> spacing , params );
2019-11-19 12:44:55 +01:00
2019-11-07 14:03:50 +01:00
// new paths must be rotated back
2019-11-19 12:44:55 +01:00
if ( std :: abs ( infill_angle ) >= EPSILON ) {
2019-11-07 14:03:50 +01:00
for ( auto it = polylines_out . begin () + polylines_out_first_idx ; it != polylines_out . end (); ++ it )
it -> rotate ( infill_angle );
}
2018-02-22 09:56:05 +01:00
}
}
} // namespace Slic3r