2018-02-22 09:56:05 +01:00
#include "../ClipperUtils.hpp"
#include "../PolylineCollection.hpp"
#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 ,
2018-04-13 13:46:31 +02:00
double z_cos , double z_sin , bool vertical )
{
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 );
2018-04-13 13:46:31 +02:00
points . pop_back ();
int n = points . size ();
do {
2018-08-21 21:05:24 +02:00
points . emplace_back ( Vec2d ( points [ points . size () - n ]( 0 ) + period , points [ points . size () - n ]( 1 )));
2018-08-17 15:53:43 +02:00
} while ( points . back ()( 0 ) < width );
points . back ()( 0 ) = width ;
2018-04-13 13:46:31 +02:00
// and construct the final polyline to return:
Polyline polyline ;
for ( auto & point : points ) {
2018-08-17 15:53:43 +02:00
point ( 1 ) += offset ;
point ( 1 ) = clamp ( 0. , height , double ( point ( 1 )));
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
2018-08-21 21:05:24 +02:00
static std :: vector < Vec2d > make_one_period ( double width , double scaleFactor , double z_cos , double z_sin , bool vertical , bool flip )
2018-02-23 18:32:35 +01:00
{
2018-08-21 21:05:24 +02:00
std :: vector < Vec2d > points ;
2018-04-13 13:46:31 +02:00
double dx = M_PI_4 ; // very coarse spacing to begin with
double limit = std :: min ( 2 * M_PI , width );
for ( double x = 0. ; x < limit + EPSILON ; x += dx ) { // so the last point is there too
x = std :: min ( x , limit );
2018-08-21 21:05:24 +02:00
points . emplace_back ( Vec2d ( x , f ( x , z_sin , z_cos , vertical , flip )));
2018-02-23 18:32:35 +01:00
}
2018-04-13 13:46:31 +02:00
// now we will check all internal points and in case some are too far from the line connecting its neighbours,
// we will add one more point on each side:
const double tolerance = .1 ;
for ( unsigned int i = 1 ; i < points . size () - 1 ; ++ i ) {
auto & lp = points [ i - 1 ]; // left point
auto & tp = points [ i ]; // this point
2018-08-21 17:43:05 +02:00
Vec2d lrv = tp - lp ;
2018-04-13 13:46:31 +02:00
auto & rp = points [ i + 1 ]; // right point
// calculate distance of the point to the line:
2018-08-21 17:43:05 +02:00
double dist_mm = unscale < double > ( scaleFactor ) * std :: abs ( cross2 ( rp , lp ) - cross2 ( rp - lp , tp )) / lrv . norm ();
2018-04-13 13:46:31 +02:00
if ( dist_mm > tolerance ) { // if the difference from straight line is more than this
2018-08-17 15:53:43 +02:00
double x = 0.5f * ( points [ i - 1 ]( 0 ) + points [ i ]( 0 ));
2018-08-21 21:05:24 +02:00
points . emplace_back ( Vec2d ( x , f ( x , z_sin , z_cos , vertical , flip )));
2018-08-17 15:53:43 +02:00
x = 0.5f * ( points [ i + 1 ]( 0 ) + points [ i ]( 0 ));
2018-08-21 21:05:24 +02:00
points . emplace_back ( Vec2d ( x , f ( x , z_sin , z_cos , vertical , flip )));
// we added the points to the end, but need them all in order
std :: sort ( points . begin (), points . end (), []( const Vec2d & lhs , const Vec2d & rhs ){ return lhs < rhs ; });
// decrement i so we also check the first newly added point
-- i ;
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 ;
2018-02-23 18:32:35 +01: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
2018-08-21 21:05:24 +02:00
std :: vector < Vec2d > one_period = make_one_period ( width , scaleFactor , z_cos , z_sin , vertical , flip ); // creates one period of the waves, so it doesn't have to be recalculated all the time
2018-04-13 13:46:31 +02:00
Polylines result ;
for ( double y0 = lower_bound ; y0 < upper_bound + EPSILON ; y0 += 2 * M_PI ) // creates odd polylines
result . emplace_back ( make_wave ( one_period , width , height , y0 , scaleFactor , z_cos , z_sin , vertical ));
flip = ! flip ; // even polylines are a bit shifted
one_period = make_one_period ( width , scaleFactor , z_cos , z_sin , vertical , flip ); // updates the one period sample
for ( double y0 = lower_bound + M_PI ; y0 < upper_bound + EPSILON ; y0 += 2 * M_PI ) // creates even polylines
result . emplace_back ( make_wave ( one_period , width , height , y0 , scaleFactor , z_cos , z_sin , vertical ));
2018-02-22 09:56:05 +01:00
return result ;
}
void FillGyroid :: _fill_surface_single (
const FillParams & params ,
unsigned int thickness_layers ,
const std :: pair < float , Point > & direction ,
ExPolygon & expolygon ,
Polylines & polylines_out )
{
2018-04-05 10:31:53 +02:00
// no rotation is supported for this infill pattern (yet)
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.
2018-04-13 13:46:31 +02:00
double density_adjusted = std :: max ( 0. , params . density * 2. );
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
2018-04-05 10:31:53 +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
2018-02-23 18:32:35 +01:00
Polylines polylines = make_gyroid_waves (
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
// move pattern in place
2018-02-23 18:32:35 +01:00
for ( Polyline & polyline : polylines )
2018-08-17 15:53:43 +02:00
polyline . translate ( bb . min ( 0 ), bb . min ( 1 ));
2018-02-22 09:56:05 +01:00
// clip pattern to boundaries
polylines = intersection_pl ( polylines , ( Polygons ) expolygon );
// connect lines
if ( ! params . dont_connect && ! polylines . empty ()) { // prevent calling leftmost_point() on empty collections
ExPolygon expolygon_off ;
{
ExPolygons expolygons_off = offset_ex ( expolygon , ( float ) SCALED_EPSILON );
if ( ! expolygons_off . empty ()) {
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
assert ( expolygons_off . size () == 1 );
std :: swap ( expolygon_off , expolygons_off . front ());
}
}
Polylines chained = PolylineCollection :: chained_path_from (
std :: move ( polylines ),
PolylineCollection :: leftmost_point ( polylines ), false ); // reverse allowed
bool first = true ;
2018-02-23 18:32:35 +01:00
for ( Polyline & polyline : chained ) {
2018-02-22 09:56:05 +01:00
if ( ! first ) {
// Try to connect the lines.
Points & pts_end = polylines_out . back (). points ;
2018-02-23 18:32:35 +01:00
const Point & first_point = polyline . points . front ();
2018-02-22 09:56:05 +01:00
const Point & last_point = pts_end . back ();
// TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions
// TODO: avoid crossing current infill path
2018-08-17 14:14:24 +02:00
if (( last_point - first_point ). cast < double > (). norm () <= 5 * distance &&
2018-02-22 09:56:05 +01:00
expolygon_off . contains ( Line ( last_point , first_point ))) {
// Append the polyline.
2018-02-23 18:32:35 +01:00
pts_end . insert ( pts_end . end (), polyline . points . begin (), polyline . points . end ());
2018-02-22 09:56:05 +01:00
continue ;
}
}
// The lines cannot be connected.
2018-02-23 18:32:35 +01:00
polylines_out . emplace_back ( std :: move ( polyline ));
2018-02-22 09:56:05 +01:00
first = false ;
}
}
}
} // namespace Slic3r