1
+ #include " ../ClipperUtils.hpp"
2
+ #include " ../ShortestPath.hpp"
3
+ #include " ../Surface.hpp"
4
+ #include < cmath>
5
+
6
+ #include " FillCrossHatch.hpp"
7
+
8
+ namespace Slic3r {
9
+
10
+ // CrossHatch Infill: Enhances 3D Printing Speed & Reduces Noise
11
+ // CrossHatch, as its name hints, alternates line direction by 90 degrees every few layers to improve adhesion.
12
+ // It introduces transform layers between direction shifts for better line cohesion, which fixes the weakness of line infill.
13
+ // The transform technique is inspired by David Eccles, improved 3D honeycomb but we made a more flexible implementation.
14
+ // This method notably increases printing speed, meeting the demands of modern high-speed 3D printers, and reduces noise for most layers.
15
+ // By Bambu Lab
16
+
17
+ // graph credits: David Eccles (gringer).
18
+ // But we made a different definition for points.
19
+ /* o---o
20
+ * / \
21
+ * / \
22
+ * \ /
23
+ * \ /
24
+ * o---o
25
+ * p1 p2 p3 p4
26
+ */
27
+
28
+ static Pointfs generate_one_cycle (double progress, coordf_t period)
29
+ {
30
+ Pointfs out;
31
+ double offset = progress * 1 . / 8 . * period;
32
+ out.reserve (4 );
33
+ out.push_back (Vec2d (0.25 * period - offset, offset));
34
+ out.push_back (Vec2d (0.25 * period + offset, offset));
35
+ out.push_back (Vec2d (0.75 * period - offset, -offset));
36
+ out.push_back (Vec2d (0.75 * period + offset, -offset));
37
+ return out;
38
+ }
39
+
40
+ static Polylines generate_transform_pattern (double inprogress, int direction, coordf_t ingrid_size, coordf_t inwidth, coordf_t inheight)
41
+ {
42
+ coordf_t width = inwidth;
43
+ coordf_t height = inheight;
44
+ coordf_t grid_size = ingrid_size * 2 ; // we due with odd and even saparately.
45
+ double progress = inprogress;
46
+ Polylines out_polylines;
47
+
48
+ // generate template patterns;
49
+ Pointfs one_cycle_points = generate_one_cycle (progress, grid_size);
50
+
51
+ Polyline one_cycle;
52
+ one_cycle.points .reserve (one_cycle_points.size ());
53
+ for (size_t i = 0 ; i < one_cycle_points.size (); i++) one_cycle.points .push_back (Point (one_cycle_points[i]));
54
+
55
+ // swap if vertical
56
+ if (direction < 0 ) {
57
+ width = height;
58
+ height = inwidth;
59
+ }
60
+
61
+ // replicate polylines;
62
+ Polylines odd_polylines;
63
+ Polyline odd_poly;
64
+ int num_of_cycle = width / grid_size + 2 ;
65
+ odd_poly.points .reserve (num_of_cycle * one_cycle.size ());
66
+
67
+ // replicate to odd line
68
+ Point translate = Point (0 , 0 );
69
+ for (size_t i = 0 ; i < num_of_cycle; i++) {
70
+ Polyline odd_points;
71
+ odd_points = Polyline (one_cycle);
72
+ odd_points.translate (Point (i * grid_size, 0.0 ));
73
+ odd_poly.points .insert (odd_poly.points .end (), odd_points.begin (), odd_points.end ());
74
+ }
75
+
76
+ // fill the height
77
+ int num_of_lines = height / grid_size + 2 ;
78
+ odd_polylines.reserve (num_of_lines * odd_poly.size ());
79
+ for (size_t i = 0 ; i < num_of_lines; i++) {
80
+ Polyline poly = odd_poly;
81
+ poly.translate (Point (0.0 , grid_size * i));
82
+ odd_polylines.push_back (poly);
83
+ }
84
+ // save to output
85
+ out_polylines.insert (out_polylines.end (), odd_polylines.begin (), odd_polylines.end ());
86
+
87
+ // replicate to even lines
88
+ Polylines even_polylines;
89
+ even_polylines.reserve (odd_polylines.size ());
90
+ for (size_t i = 0 ; i < odd_polylines.size (); i++) {
91
+ Polyline even = odd_poly;
92
+ even.translate (Point (-0.5 * grid_size, (i + 0.5 ) * grid_size));
93
+ even_polylines.push_back (even);
94
+ }
95
+
96
+ // save for output
97
+ out_polylines.insert (out_polylines.end (), even_polylines.begin (), even_polylines.end ());
98
+
99
+ // change to vertical if need
100
+ if (direction < 0 ) {
101
+ // swap xy, see if we need better performance method
102
+ for (Polyline &poly : out_polylines) {
103
+ for (Point &p : poly) { std::swap (p.x (), p.y ()); }
104
+ }
105
+ }
106
+
107
+ return out_polylines;
108
+ }
109
+
110
+ static Polylines generate_repeat_pattern (int direction, coordf_t grid_size, coordf_t inwidth, coordf_t inheight)
111
+ {
112
+ coordf_t width = inwidth;
113
+ coordf_t height = inheight;
114
+ Polylines out_polylines;
115
+
116
+ // swap if vertical
117
+ if (direction < 0 ) {
118
+ width = height;
119
+ height = inwidth;
120
+ }
121
+
122
+ int num_of_lines = height / grid_size + 1 ;
123
+ out_polylines.reserve (num_of_lines);
124
+
125
+ for (int i = 0 ; i < num_of_lines; i++) {
126
+ Polyline poly;
127
+ poly.points .reserve (2 );
128
+ poly.append (Point (coordf_t (0 ), coordf_t (grid_size * i)));
129
+ poly.append (Point (width, coordf_t (grid_size * i)));
130
+ out_polylines.push_back (poly);
131
+ }
132
+
133
+ // change to vertical if needed
134
+ if (direction < 0 ) {
135
+ // swap xy
136
+ for (Polyline &poly : out_polylines) {
137
+ for (Point &p : poly) { std::swap (p.x (), p.y ()); }
138
+ }
139
+ }
140
+
141
+ return out_polylines;
142
+ }
143
+
144
+ // it makes the real patterns that overlap the bounding box
145
+ // repeat_ratio define the ratio between the height of repeat pattern and grid
146
+ static Polylines generate_infill_layers (coordf_t z_height, double repeat_ratio, coordf_t grid_size, coordf_t width, coordf_t height)
147
+ {
148
+ Polylines result;
149
+ coordf_t trans_layer_size = grid_size * 0.4 ; // upper.
150
+ coordf_t repeat_layer_size = grid_size * repeat_ratio; // lower.
151
+ z_height += repeat_layer_size / 2 ; // offset to improve first few layer strength
152
+ coordf_t period = trans_layer_size + repeat_layer_size;
153
+ coordf_t remains = z_height - std::floor (z_height / period) * period;
154
+ coordf_t trans_z = remains - repeat_layer_size; // put repeat layer first.
155
+ coordf_t repeat_z = remains;
156
+
157
+ int phase = fmod (z_height, period * 2 ) - (period - 1 ); // add epsilon
158
+ int direction = phase <= 0 ? -1 : 1 ;
159
+
160
+ // this is a repeat layer
161
+ if (trans_z < 0 ) {
162
+ result = generate_repeat_pattern (direction, grid_size, width, height);
163
+ }
164
+ // this is a transform layer
165
+ else {
166
+ double progress = fmod (trans_z, trans_layer_size) / trans_layer_size;
167
+
168
+ // split the progress to forward and backward, with a opposite direction.
169
+ if (progress < 0.5 )
170
+ result = generate_transform_pattern ((progress + 0.1 ) * 2 , direction, grid_size, width, height); // increase overlapping.
171
+ else
172
+ result = generate_transform_pattern ((1.1 - progress) * 2 , -1 * direction, grid_size, width, height);
173
+ }
174
+
175
+ return result;
176
+ }
177
+
178
+ void FillCrossHatch ::_fill_surface_single (
179
+ const FillParams ¶ms, unsigned int thickness_layers, const std::pair<float , Point> &direction, ExPolygon expolygon, Polylines &polylines_out)
180
+ {
181
+ // rotate angle
182
+ auto infill_angle = float (this ->angle );
183
+ if (std::abs (infill_angle) >= EPSILON) expolygon.rotate (-infill_angle);
184
+
185
+ // get the rotated bounding box
186
+ BoundingBox bb = expolygon.contour .bounding_box ();
187
+
188
+ // linespace modifier
189
+ coord_t line_spacing = coord_t (scale_ (this ->spacing ) / params.density );
190
+
191
+ // reduce density
192
+ if (params.density < 0.999 ) line_spacing *= 1.08 ;
193
+
194
+ bb.merge (align_to_grid (bb.min , Point (line_spacing * 4 , line_spacing * 4 )));
195
+
196
+ // generate pattern
197
+ // Orca: optimize the cross-hatch infill pattern to improve strength when low infill density is used.
198
+ double repeat_ratio = 1.0 ;
199
+ if (params.density < 0.3 )
200
+ repeat_ratio = std::clamp (1.0 - std::exp (-5 * params.density ), 0.2 , 1.0 );
201
+
202
+ Polylines polylines = generate_infill_layers (scale_ (this ->z ), repeat_ratio, line_spacing, bb.size ()(0 ), bb.size ()(1 ));
203
+
204
+ // shift the pattern to the actual space
205
+ for (Polyline &pl : polylines) { pl.translate (bb.min ); }
206
+
207
+ polylines = intersection_pl (polylines, to_polygons (expolygon));
208
+
209
+ // --- remove small remains from gyroid infill
210
+ if (!polylines.empty ()) {
211
+ // Remove very small bits, but be careful to not remove infill lines connecting thin walls!
212
+ // The infill perimeter lines should be separated by around a single infill line width.
213
+ const double minlength = scale_ (0.8 * this ->spacing );
214
+ polylines.erase (std::remove_if (polylines.begin (), polylines.end (), [minlength](const Polyline &pl)
215
+ { return pl.length () < minlength; }), polylines.end ());
216
+ }
217
+
218
+ if (!polylines.empty ()) {
219
+ int infill_start_idx = polylines_out.size (); // only rotate what belongs to us.
220
+ // connect lines
221
+ if (params.dont_connect () || polylines.size () <= 1 )
222
+ append (polylines_out, chain_polylines (std::move (polylines)));
223
+ else
224
+ this ->connect_infill (std::move (polylines), expolygon, polylines_out, this ->spacing , params);
225
+
226
+ // rotate back
227
+ if (std::abs (infill_angle) >= EPSILON) {
228
+ for (auto it = polylines_out.begin () + infill_start_idx; it != polylines_out.end (); ++it) it->rotate (infill_angle);
229
+ }
230
+ }
231
+ }
232
+
233
+ } // namespace Slic3r
0 commit comments