-
-
Notifications
You must be signed in to change notification settings - Fork 919
Expand file tree
/
Copy pathTopSurface.cpp
More file actions
191 lines (178 loc) · 7.91 KB
/
TopSurface.cpp
File metadata and controls
191 lines (178 loc) · 7.91 KB
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
// Copyright (c) 2024 UltiMaker
// CuraEngine is released under the terms of the AGPLv3 or higher
#include "TopSurface.h"
#include "ExtruderTrain.h"
#include "LayerPlan.h"
#include "geometry/OpenPolyline.h"
#include "infill.h"
#include "sliceDataStorage.h"
namespace cura
{
TopSurface::TopSurface()
{
// Do nothing. Areas stays empty.
}
void TopSurface::setAreasFromMeshAndLayerNumber(SliceMeshStorage& mesh, size_t layer_number)
{
// The top surface is all parts of the mesh where there's no mesh above it, so find the layer above it first.
Shape mesh_above;
if (layer_number < mesh.layers.size() - 1)
{
mesh_above = mesh.layers[layer_number + 1].getOutlines();
} // If this is the top-most layer, mesh_above stays empty.
if (mesh.settings.get<bool>("magic_spiralize"))
{
// when spiralizing, the model is often solid so it's no good trying to determine if there is air above or not
// in this situation, just iron the topmost of the bottom layers
if (layer_number == mesh.settings.get<size_t>("initial_bottom_layers") - 1)
{
areas = mesh.layers[layer_number].getOutlines();
}
}
else
{
areas = mesh.layers[layer_number].getOutlines().difference(mesh_above);
}
}
bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const GCodePathConfig& line_config, LayerPlan& layer) const
{
if (areas.empty())
{
return false; // Nothing to do.
}
// Generate the lines to cover the surface.
const int extruder_nr = mesh.settings.get<ExtruderTrain&>("top_bottom_extruder_nr").extruder_nr_;
const EFillMethod pattern = mesh.settings.get<EFillMethod>("ironing_pattern");
const bool zig_zaggify_infill = pattern == EFillMethod::ZIG_ZAG;
constexpr bool connect_polygons = false; // midway connections can make the surface less smooth
const coord_t line_spacing = mesh.settings.get<coord_t>("ironing_line_spacing");
const coord_t line_width = line_config.getLineWidth();
const size_t roofing_layer_count = std::min(mesh.settings.get<size_t>("roofing_layer_count"), mesh.settings.get<size_t>("top_layers"));
const std::vector<AngleDegrees>& top_most_skin_angles = (roofing_layer_count > 0) ? mesh.roofing_angles : mesh.skin_angles;
assert(top_most_skin_angles.size() > 0);
const AngleDegrees direction = top_most_skin_angles[layer.getLayerNr() % top_most_skin_angles.size()] + AngleDegrees(90.0); // Always perpendicular to the skin lines.
constexpr coord_t infill_overlap = 0;
constexpr int infill_multiplier = 1;
constexpr coord_t shift = 0;
const coord_t max_resolution = mesh.settings.get<coord_t>("meshfix_maximum_resolution");
const coord_t max_deviation = mesh.settings.get<coord_t>("meshfix_maximum_deviation");
const Ratio ironing_flow = mesh.settings.get<Ratio>("ironing_flow");
const bool enforce_monotonic_order = mesh.settings.get<bool>("ironing_monotonic");
constexpr size_t wall_line_count = 0;
const coord_t small_area_width = 0; // This shouldn't be on for ironing.
const Point2LL infill_origin = Point2LL();
const bool skip_line_stitching = enforce_monotonic_order;
coord_t ironing_inset = -mesh.settings.get<coord_t>("ironing_inset");
if (pattern == EFillMethod::ZIG_ZAG)
{
// Compensate for the outline_offset decrease that takes place when using the infill generator to generate ironing with the zigzag pattern
const Ratio width_scale = (double)mesh.settings.get<coord_t>("layer_height") / mesh.settings.get<coord_t>("infill_sparse_thickness");
ironing_inset += width_scale * line_width / 2;
// Align the edge of the ironing line with the edge of the outer wall
ironing_inset -= ironing_flow * line_width / 2;
}
else if (pattern == EFillMethod::CONCENTRIC)
{
// Counteract the outline_offset increase that takes place when using the infill generator to generate ironing with the concentric pattern
ironing_inset += line_spacing - line_width / 2;
// Align the edge of the ironing line with the edge of the outer wall
ironing_inset -= ironing_flow * line_width / 2;
}
Shape ironed_areas = areas.offset(ironing_inset);
Infill infill_generator(
pattern,
zig_zaggify_infill,
connect_polygons,
ironed_areas,
line_width,
line_spacing,
infill_overlap,
infill_multiplier,
direction,
layer.z_ - 10,
shift,
max_resolution,
max_deviation,
wall_line_count,
small_area_width,
infill_origin,
skip_line_stitching);
std::vector<VariableWidthLines> ironing_paths;
Shape ironing_polygons;
OpenLinesSet ironing_lines;
infill_generator.generate(ironing_paths, ironing_polygons, ironing_lines, mesh.settings, layer.getLayerNr(), SectionType::IRONING);
if (ironing_polygons.empty() && ironing_lines.empty() && ironing_paths.empty())
{
return false; // Nothing to do.
}
bool added = false;
if (! ironing_polygons.empty())
{
layer.addTravel(ironing_polygons[0][0]);
layer.addPolygonsByOptimizer(ironing_polygons, line_config, mesh.settings, ZSeamConfig());
added = true;
}
if (! ironing_lines.empty())
{
if (pattern == EFillMethod::LINES || pattern == EFillMethod::ZIG_ZAG)
{
// Move to a corner of the area that is perpendicular to the ironing lines, to reduce the number of seams.
const AABB bounding_box(ironed_areas);
PointMatrix rotate(-direction + 90);
const Point2LL center = bounding_box.getMiddle();
const Point2LL far_away = rotate.apply(
Point2LL(0, vSize(bounding_box.max_ - center) * 100)); // Some direction very far away in the direction perpendicular to the ironing lines, relative to the centre.
// Two options to start, both perpendicular to the ironing lines. Which is closer?
const Point2LL front_side = PolygonUtils::findNearestVert(center + far_away, ironed_areas).p();
const Point2LL back_side = PolygonUtils::findNearestVert(center - far_away, ironed_areas).p();
if (vSize2(layer.getLastPlannedPositionOrStartingPosition() - front_side) < vSize2(layer.getLastPlannedPositionOrStartingPosition() - back_side))
{
layer.addTravel(front_side);
}
else
{
layer.addTravel(back_side);
}
}
if (! enforce_monotonic_order)
{
layer.addLinesByOptimizer(ironing_lines, line_config, SpaceFillType::PolyLines);
}
else
{
const coord_t max_adjacent_distance
= line_spacing * 1.1; // Lines are considered adjacent - meaning they need to be printed in monotonic order - if spaced 1 line apart, with 10% extra play.
layer.addLinesMonotonic(Shape(), ironing_lines, line_config, SpaceFillType::PolyLines, AngleRadians(direction), max_adjacent_distance);
}
added = true;
}
if (! ironing_paths.empty())
{
constexpr coord_t wipe_dist = 0u;
const ZSeamConfig z_seam_config(EZSeamType::SHORTEST, layer.getLastPlannedPositionOrStartingPosition(), EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER, false);
InsetOrderOptimizer wall_orderer(
storage,
layer,
mesh.settings,
extruder_nr,
line_config,
line_config,
line_config,
line_config,
line_config,
line_config,
line_config,
line_config,
wipe_dist,
wipe_dist,
extruder_nr,
extruder_nr,
z_seam_config,
ironing_paths,
storage.getModelBoundingBox().flatten().getMiddle());
wall_orderer.addToLayer();
added = true;
}
return added;
}
} // namespace cura