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 ¶ms,
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