1 #include "../ClipperUtils.hpp"
2 #include "../ShortestPath.hpp"
3 #include "../Surface.hpp"
4 #include <cmath>
5 #include <algorithm>
6 #include <iostream>
7 
8 #include "FillGyroid.hpp"
9 
10 namespace Slic3r {
11 
f(double x,double z_sin,double z_cos,bool vertical,bool flip)12 static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
13 {
14     if (vertical) {
15         double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
16         double a   = sin(x + phase_offset);
17         double b   = - z_cos;
18         double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
19         double r   = sqrt(sqr(a) + sqr(b));
20         return asin(a/r) + asin(res/r) + M_PI;
21     }
22     else {
23         double phase_offset = z_sin < 0 ? M_PI : 0.;
24         double a   = cos(x + phase_offset);
25         double b   = - z_sin;
26         double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
27         double r   = sqrt(sqr(a) + sqr(b));
28         return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
29     }
30 }
31 
make_wave(const std::vector<Vec2d> & one_period,double width,double height,double offset,double scaleFactor,double z_cos,double z_sin,bool vertical,bool flip)32 static inline Polyline make_wave(
33     const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
34     double z_cos, double z_sin, bool vertical, bool flip)
35 {
36     std::vector<Vec2d> points = one_period;
37     double period = points.back()(0);
38     if (width != period) // do not extend if already truncated
39     {
40         points.reserve(one_period.size() * size_t(floor(width / period)));
41         points.pop_back();
42 
43         size_t n = points.size();
44         do {
45             points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
46         } while (points.back()(0) < width - EPSILON);
47 
48         points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
49     }
50 
51     // and construct the final polyline to return:
52     Polyline polyline;
53     polyline.points.reserve(points.size());
54     for (auto& point : points) {
55         point(1) += offset;
56         point(1) = clamp(0., height, double(point(1)));
57         if (vertical)
58             std::swap(point(0), point(1));
59         polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
60     }
61 
62     return polyline;
63 }
64 
make_one_period(double width,double scaleFactor,double z_cos,double z_sin,bool vertical,bool flip,double tolerance)65 static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
66 {
67     std::vector<Vec2d> points;
68     double dx = M_PI_2; // exact coordinates on main inflexion lobes
69     double limit = std::min(2*M_PI, width);
70     points.reserve(coord_t(ceil(limit / tolerance / 3)));
71 
72     for (double x = 0.; x < limit - EPSILON; x += dx) {
73         points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
74     }
75     points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip)));
76 
77     // piecewise increase in resolution up to requested tolerance
78     for(;;)
79     {
80         size_t size = points.size();
81         for (unsigned int i = 1;i < size; ++i) {
82             auto& lp = points[i-1]; // left point
83             auto& rp = points[i];   // right point
84             double x = lp(0) + (rp(0) - lp(0)) / 2;
85             double y = f(x, z_sin, z_cos, vertical, flip);
86             Vec2d ip = {x, y};
87             if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) {
88                 points.emplace_back(std::move(ip));
89             }
90         }
91 
92         if (size == points.size())
93             break;
94         else
95         {
96             // insert new points in order
97             std::sort(points.begin(), points.end(),
98                       [](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); });
99         }
100     }
101 
102     return points;
103 }
104 
make_gyroid_waves(double gridZ,double density_adjusted,double line_spacing,double width,double height)105 static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
106 {
107     const double scaleFactor = scale_(line_spacing) / density_adjusted;
108 
109     // tolerance in scaled units. clamp the maximum tolerance as there's
110     // no processing-speed benefit to do so beyond a certain point
111     const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale<double>(scaleFactor);
112 
113     //scale factor for 5% : 8 712 388
114     // 1z = 10^-6 mm ?
115     const double z     = gridZ / scaleFactor;
116     const double z_sin = sin(z);
117     const double z_cos = cos(z);
118 
119     bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
120     double lower_bound = 0.;
121     double upper_bound = height;
122     bool flip = true;
123     if (vertical) {
124         flip = false;
125         lower_bound = -M_PI;
126         upper_bound = width - M_PI_2;
127         std::swap(width,height);
128     }
129 
130     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
131     flip = !flip;                                                                   // even polylines are a bit shifted
132     std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
133     Polylines result;
134 
135     for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
136         // creates odd polylines
137         result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
138         // creates even polylines
139         y0 += M_PI;
140         if (y0 < upper_bound + EPSILON) {
141             result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
142         }
143     }
144 
145     return result;
146 }
147 
148 // FIXME: needed to fix build on Mac on buildserver
149 constexpr double FillGyroid::PatternTolerance;
150 
_fill_surface_single(const FillParams & params,unsigned int thickness_layers,const std::pair<float,Point> & direction,ExPolygon expolygon,Polylines & polylines_out)151 void FillGyroid::_fill_surface_single(
152     const FillParams                &params,
153     unsigned int                     thickness_layers,
154     const std::pair<float, Point>   &direction,
155     ExPolygon                        expolygon,
156     Polylines                       &polylines_out)
157 {
158     auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
159     if(std::abs(infill_angle) >= EPSILON)
160         expolygon.rotate(-infill_angle);
161 
162     BoundingBox bb = expolygon.contour.bounding_box();
163     // Density adjusted to have a good %of weight.
164     double      density_adjusted = std::max(0., params.density * DensityAdjust);
165     // Distance between the gyroid waves in scaled coordinates.
166     coord_t     distance = coord_t(scale_(this->spacing) / density_adjusted);
167 
168     // align bounding box to a multiple of our grid module
169     bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
170 
171     // generate pattern
172     Polylines polylines = make_gyroid_waves(
173         scale_(this->z),
174         density_adjusted,
175         this->spacing,
176         ceil(bb.size()(0) / distance) + 1.,
177         ceil(bb.size()(1) / distance) + 1.);
178 
179 	// shift the polyline to the grid origin
180 	for (Polyline &pl : polylines)
181 		pl.translate(bb.min);
182 
183 	polylines = intersection_pl(polylines, to_polygons(expolygon));
184 
185     if (! polylines.empty()) {
186 		// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
187         // The infill perimeter lines should be separated by around a single infill line width.
188         const double minlength = scale_(0.8 * this->spacing);
189 		polylines.erase(
190 			std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
191 			polylines.end());
192     }
193 
194 	if (! polylines.empty()) {
195 		// connect lines
196 		size_t polylines_out_first_idx = polylines_out.size();
197 		if (params.dont_connect())
198         	append(polylines_out, chain_polylines(polylines));
199         else
200             this->connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
201 
202 	    // new paths must be rotated back
203         if (std::abs(infill_angle) >= EPSILON) {
204 	        for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
205 	        	it->rotate(infill_angle);
206 	    }
207     }
208 }
209 
210 } // namespace Slic3r
211