forked from Ultimaker/CuraEngine
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathGyroidInfill.cpp
413 lines (380 loc) · 21.1 KB
/
GyroidInfill.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
#include "GyroidInfill.h"
#include "../utils/AABB.h"
#include "../utils/linearAlg2D.h"
#include "../utils/polygon.h"
namespace cura {
GyroidInfill::GyroidInfill() {
}
GyroidInfill::~GyroidInfill() {
}
void GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_zaggify, coord_t outline_offset, coord_t infill_line_width, coord_t line_distance, const Polygons& in_outline, coord_t z)
{
// generate infill based on the gyroid equation: sin_x * cos_y + sin_y * cos_z + sin_z * cos_x = 0
// kudos to the author of the Slic3r implementation equation code, the equation code here is based on that
const Polygons outline = in_outline.offset(outline_offset);
const AABB aabb(outline);
int pitch = line_distance * 2.41; // this produces similar density to the "line" infill pattern
int num_steps = 4;
int step = pitch / num_steps;
while (step > 500 && num_steps < 16)
{
num_steps *= 2;
step = pitch / num_steps;
}
pitch = step * num_steps; // recalculate to avoid precision errors
const double z_rads = 2 * M_PI * z / pitch;
const double cos_z = std::cos(z_rads);
const double sin_z = std::sin(z_rads);
std::vector<coord_t> odd_line_coords;
std::vector<coord_t> even_line_coords;
Polygons result;
std::vector<Point> chains[2]; // [start_points[], end_points[]]
std::vector<unsigned> connected_to[2]; // [chain_indices[], chain_indices[]]
std::vector<int> line_numbers; // which row/column line a chain is part of
if (std::abs(sin_z) <= std::abs(cos_z))
{
// "vertical" lines
const double phase_offset = ((cos_z < 0) ? M_PI : 0) + M_PI;
for (coord_t y = 0; y < pitch; y += step)
{
const double y_rads = 2 * M_PI * y / pitch;
const double a = cos_z;
const double b = std::sin(y_rads + phase_offset);
const double odd_c = sin_z * std::cos(y_rads + phase_offset);
const double even_c = sin_z * std::cos(y_rads + phase_offset + M_PI);
const double h = std::sqrt(a * a + b * b);
const double odd_x_rads = ((h != 0) ? std::asin(odd_c / h) + std::asin(b / h) : 0) - M_PI/2;
const double even_x_rads = ((h != 0) ? std::asin(even_c / h) + std::asin(b / h) : 0) - M_PI/2;
odd_line_coords.push_back(odd_x_rads / M_PI * pitch);
even_line_coords.push_back(even_x_rads / M_PI * pitch);
}
const unsigned num_coords = odd_line_coords.size();
unsigned num_columns = 0;
for (coord_t x = (std::floor(aabb.min.X / pitch) - 2.25) * pitch; x <= aabb.max.X + pitch/2; x += pitch/2)
{
bool is_first_point = true;
Point last;
bool last_inside = false;
unsigned chain_end_index = 0;
Point chain_end[2];
for (coord_t y = (std::floor(aabb.min.Y / pitch) - 1) * pitch; y <= aabb.max.Y + pitch; y += pitch)
{
for (unsigned i = 0; i < num_coords; ++i)
{
Point current(x + ((num_columns & 1) ? odd_line_coords[i] : even_line_coords[i])/2 + pitch, y + (coord_t)(i * step));
bool current_inside = outline.inside(current, true);
if (!is_first_point)
{
if (last_inside && current_inside)
{
// line doesn't hit the boundary, add the whole line
result.addLine(last, current);
}
else if (last_inside != current_inside)
{
// line hits the boundary, add the part that's inside the boundary
Polygons line;
line.addLine(last, current);
line = outline.intersectionPolyLines(line);
if (line.size() > 0)
{
// some of the line is inside the boundary
result.addLine(line[0][0], line[0][1]);
if (zig_zaggify)
{
chain_end[chain_end_index] = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1];
if (++chain_end_index == 2)
{
chains[0].push_back(chain_end[0]);
chains[1].push_back(chain_end[1]);
chain_end_index = 0;
connected_to[0].push_back(std::numeric_limits<unsigned>::max());
connected_to[1].push_back(std::numeric_limits<unsigned>::max());
line_numbers.push_back(num_columns);
}
}
}
else
{
// none of the line is inside the boundary so the point that's actually on the boundary
// is the chain end
if (zig_zaggify)
{
chain_end[chain_end_index] = (last_inside) ? last : current;
if (++chain_end_index == 2)
{
chains[0].push_back(chain_end[0]);
chains[1].push_back(chain_end[1]);
chain_end_index = 0;
connected_to[0].push_back(std::numeric_limits<unsigned>::max());
connected_to[1].push_back(std::numeric_limits<unsigned>::max());
line_numbers.push_back(num_columns);
}
}
}
}
}
last = current;
last_inside = current_inside;
is_first_point = false;
}
}
++num_columns;
}
}
else
{
// "horizontal" lines
const double phase_offset = (sin_z < 0) ? M_PI : 0;
for (coord_t x = 0; x < pitch; x += step)
{
const double x_rads = 2 * M_PI * x / pitch;
const double a = sin_z;
const double b = std::cos(x_rads + phase_offset);
const double odd_c = cos_z * std::sin(x_rads + phase_offset + M_PI);
const double even_c = cos_z * std::sin(x_rads + phase_offset);
const double h = std::sqrt(a * a + b * b);
const double odd_y_rads = ((h != 0) ? std::asin(odd_c / h) + std::asin(b / h) : 0) + M_PI/2;
const double even_y_rads = ((h != 0) ? std::asin(even_c / h) + std::asin(b / h) : 0) + M_PI/2;
odd_line_coords.push_back(odd_y_rads / M_PI * pitch);
even_line_coords.push_back(even_y_rads / M_PI * pitch);
}
const unsigned num_coords = odd_line_coords.size();
unsigned num_rows = 0;
for (coord_t y = (std::floor(aabb.min.Y / pitch) - 1) * pitch; y <= aabb.max.Y + pitch/2; y += pitch/2)
{
bool is_first_point = true;
Point last;
bool last_inside = false;
unsigned chain_end_index = 0;
Point chain_end[2];
for (coord_t x = (std::floor(aabb.min.X / pitch) - 1) * pitch; x <= aabb.max.X + pitch; x += pitch)
{
for (unsigned i = 0; i < num_coords; ++i)
{
Point current(x + (coord_t)(i * step), y + ((num_rows & 1) ? odd_line_coords[i] : even_line_coords[i])/2);
bool current_inside = outline.inside(current, true);
if (!is_first_point)
{
if (last_inside && current_inside)
{
// line doesn't hit the boundary, add the whole line
result.addLine(last, current);
}
else if (last_inside != current_inside)
{
// line hits the boundary, add the part that's inside the boundary
Polygons line;
line.addLine(last, current);
line = outline.intersectionPolyLines(line);
if (line.size() > 0)
{
// some of the line is inside the boundary
result.addLine(line[0][0], line[0][1]);
if (zig_zaggify)
{
chain_end[chain_end_index] = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1];
if (++chain_end_index == 2)
{
chains[0].push_back(chain_end[0]);
chains[1].push_back(chain_end[1]);
chain_end_index = 0;
connected_to[0].push_back(std::numeric_limits<unsigned>::max());
connected_to[1].push_back(std::numeric_limits<unsigned>::max());
line_numbers.push_back(num_rows);
}
}
}
else
{
// none of the line is inside the boundary so the point that's actually on the boundary
// is the chain end
if (zig_zaggify)
{
chain_end[chain_end_index] = (last_inside) ? last : current;
if (++chain_end_index == 2)
{
chains[0].push_back(chain_end[0]);
chains[1].push_back(chain_end[1]);
chain_end_index = 0;
connected_to[0].push_back(std::numeric_limits<unsigned>::max());
connected_to[1].push_back(std::numeric_limits<unsigned>::max());
line_numbers.push_back(num_rows);
}
}
}
}
}
last = current;
last_inside = current_inside;
is_first_point = false;
}
}
++num_rows;
}
}
if (zig_zaggify && chains[0].size() > 0)
{
// zig-zaggification consists of joining alternate chain ends to make a chain of chains
// the basic algorithm is that we follow the infill area boundary and as we progress we are either drawing a connector or not
// whenever we come across the end of a chain we toggle the connector drawing state
// things are made more complicated by the fact that we want to avoid generating loops and so we need to keep track
// of the indentity of the first chain in a connected sequence
int chain_ends_remaining = chains[0].size() * 2;
for (ConstPolygonRef outline_poly : outline)
{
std::vector<Point> connector_points; // the points that make up a connector line
// we need to remember the first chain processed and the path to it from the first outline point
// so that later we can possibly connect to it from the last chain processed
unsigned first_chain_chain_index = std::numeric_limits<unsigned>::max();
std::vector<Point> path_to_first_chain;
bool drawing = false; // true when a connector line is being (potentially) created
// keep track of the chain+point that a connector line started at
unsigned connector_start_chain_index = std::numeric_limits<unsigned>::max();
unsigned connector_start_point_index = std::numeric_limits<unsigned>::max();
Point cur_point; // current point of interest - either an outline point or a chain end
// go round all of the region's outline and find the chain ends that meet it
// quit the loop early if we have seen all the chain ends and are not currently drawing a connector
for (unsigned outline_point_index = 0; (chain_ends_remaining > 0 || drawing) && outline_point_index < outline_poly.size(); ++outline_point_index)
{
Point op0 = outline_poly[outline_point_index];
Point op1 = outline_poly[(outline_point_index + 1) % outline_poly.size()];
std::vector<unsigned> points_on_outline_chain_index;
std::vector<unsigned> points_on_outline_point_index;
// collect the chain ends that meet this segment of the outline
for (unsigned chain_index = 0; chain_index < chains[0].size(); ++chain_index)
{
for (unsigned point_index = 0; point_index < 2; ++point_index)
{
// don't include chain ends that are close to the segment but are beyond the segment ends
short beyond = 0;
if (LinearAlg2D::getDist2FromLineSegment(op0, chains[point_index][chain_index], op1, &beyond) < 10 && !beyond)
{
points_on_outline_point_index.push_back(point_index);
points_on_outline_chain_index.push_back(chain_index);
}
}
}
if (outline_point_index == 0 || vSize2(op0 - cur_point) > 100)
{
// this is either the first outline point or it is another outline point that is not too close to cur_point
if (first_chain_chain_index == std::numeric_limits<unsigned>::max())
{
// include the outline point in the path to the first chain
path_to_first_chain.push_back(op0);
}
cur_point = op0;
if (drawing)
{
// include the start point of this outline segment in the connector
connector_points.push_back(op0);
}
}
// iterate through each of the chain ends that meet the current outline segment
while (points_on_outline_chain_index.size() > 0)
{
// find the nearest chain end to the current point
unsigned nearest_point_index = 0;
float nearest_point_dist2 = std::numeric_limits<float>::infinity();
for (unsigned pi = 0; pi < points_on_outline_chain_index.size(); ++pi)
{
float dist2 = vSize2f(chains[points_on_outline_point_index[pi]][points_on_outline_chain_index[pi]] - cur_point);
if (dist2 < nearest_point_dist2)
{
nearest_point_dist2 = dist2;
nearest_point_index = pi;
}
}
const unsigned point_index = points_on_outline_point_index[nearest_point_index];
const unsigned chain_index = points_on_outline_chain_index[nearest_point_index];
// make the chain end the current point and add it to the connector line
cur_point = chains[point_index][chain_index];
if (drawing && connector_points.size() > 0 && vSize2(cur_point - connector_points.back()) < 100)
{
// this chain end will be too close to the last connector point so throw away the last connector point
connector_points.pop_back();
}
connector_points.push_back(cur_point);
if (first_chain_chain_index == std::numeric_limits<unsigned>::max())
{
// this is the first chain to be processed, remember it
first_chain_chain_index = chain_index;
path_to_first_chain.push_back(cur_point);
}
if (drawing)
{
// add the connector line segments but only if
// 1 - the start/end points are not the opposite ends of the same chain
// 2 - the other end of the current chain is not connected to the chain the connector line is coming from
if (chain_index != connector_start_chain_index && connected_to[(point_index + 1) % 2][chain_index] != connector_start_chain_index)
{
for (unsigned pi = 1; pi < connector_points.size(); ++pi)
{
result.addLine(connector_points[pi - 1], connector_points[pi]);
}
drawing = false;
connector_points.clear();
// remember the connection
connected_to[point_index][chain_index] = connector_start_chain_index;
connected_to[connector_start_point_index][connector_start_chain_index] = chain_index;
}
else
{
// start a new connector from the current location
connector_points.clear();
connector_points.push_back(cur_point);
// remember the chain+point that the connector started from
connector_start_chain_index = chain_index;
connector_start_point_index = point_index;
}
}
else
{
// we have just jumped a gap so now we want to start drawing again
drawing = true;
// if this connector is the first to be created or we are not connecting chains from the same row/column,
// remember the chain+point that this connector is starting from
if (connector_start_chain_index == std::numeric_limits<unsigned>::max() || line_numbers[chain_index] != line_numbers[connector_start_chain_index])
{
connector_start_chain_index = chain_index;
connector_start_point_index = point_index;
}
}
// done with this chain end
points_on_outline_chain_index.erase(points_on_outline_chain_index.begin() + nearest_point_index);
points_on_outline_point_index.erase(points_on_outline_point_index.begin() + nearest_point_index);
// decrement total amount of work to do
--chain_ends_remaining;
}
}
// we have now visited all the points in the outline, if a connector was (potentially) being drawn
// check whether the first chain is already connected to the last chain and, if not, draw the
// connector between
if (drawing && first_chain_chain_index != std::numeric_limits<unsigned>::max()
&& first_chain_chain_index != connector_start_chain_index
&& connected_to[0][first_chain_chain_index] != connector_start_chain_index
&& connected_to[1][first_chain_chain_index] != connector_start_chain_index)
{
// output the connector line segments from the last chain to the first point in the outline
connector_points.push_back(outline_poly[0]);
for (unsigned pi = 1; pi < connector_points.size(); ++pi)
{
result.addLine(connector_points[pi - 1], connector_points[pi]);
}
// output the connector line segments from the first point in the outline to the first chain
for (unsigned pi = 1; pi < path_to_first_chain.size(); ++pi)
{
result.addLine(path_to_first_chain[pi - 1], path_to_first_chain[pi]);
}
}
if (chain_ends_remaining < 1)
{
break;
}
}
}
result_lines = result;
}
} // namespace cura