Skip to content

Commit 2e4f84e

Browse files
committed
[FEATURE] Port CrossHatch infill pattern from BambuSlicer
1 parent a4d5af4 commit 2e4f84e

File tree

7 files changed

+284
-5
lines changed

7 files changed

+284
-5
lines changed

src/libslic3r/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ set(SLIC3R_SOURCES
9696
Fill/FillBase.hpp
9797
Fill/FillConcentric.cpp
9898
Fill/FillConcentric.hpp
99+
Fill/FillCrossHatch.cpp
100+
Fill/FillCrossHatch.hpp
99101
Fill/FillEnsuring.cpp
100102
Fill/FillEnsuring.hpp
101103
Fill/FillHoneycomb.cpp

src/libslic3r/Fill/FillBase.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "libslic3r/libslic3r.h"
2727
#include "FillBase.hpp"
2828
#include "FillConcentric.hpp"
29+
#include "FillCrossHatch.hpp"
2930
#include "FillHoneycomb.hpp"
3031
#include "Fill3DHoneycomb.hpp"
3132
#include "FillGyroid.hpp"
@@ -52,6 +53,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
5253
case ipGyroid: return new FillGyroid();
5354
case ipRectilinear: return new FillRectilinear();
5455
case ipAlignedRectilinear: return new FillAlignedRectilinear();
56+
case ipCrossHatch: return new FillCrossHatch();
5557
case ipMonotonic: return new FillMonotonic();
5658
case ipMonotonicLines: return new FillMonotonicLines();
5759
case ipLine: return new FillLine();

src/libslic3r/Fill/FillCrossHatch.cpp

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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 &params, 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

src/libslic3r/Fill/FillCrossHatch.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef slic3r_FillCrossHatch_hpp_
2+
#define slic3r_FillCrossHatch_hpp_
3+
4+
#include <map>
5+
6+
#include "../libslic3r.h"
7+
8+
#include "FillBase.hpp"
9+
10+
namespace Slic3r {
11+
12+
class FillCrossHatch : public Fill
13+
{
14+
public:
15+
Fill *clone() const override { return new FillCrossHatch(*this); };
16+
~FillCrossHatch() override {}
17+
18+
bool is_self_crossing() override { return false; }
19+
20+
protected:
21+
void _fill_surface_single(
22+
const FillParams &params,
23+
unsigned int thickness_layers,
24+
const std::pair<float, Point> &direction,
25+
ExPolygon expolygon,
26+
Polylines &polylines_out) override;
27+
};
28+
29+
} // namespace Slic3r
30+
31+
#endif // slic3r_FillCrossHatch_hpp_

src/libslic3r/PrintConfig.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ static const t_config_enum_values s_keys_map_InfillPattern {
147147
{ "adaptivecubic", ipAdaptiveCubic },
148148
{ "supportcubic", ipSupportCubic },
149149
{ "lightning", ipLightning },
150-
{ "zigzag", ipZigZag }
150+
{ "zigzag", ipZigZag },
151+
{ "crosshatch", ipCrossHatch }
151152
};
152153
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
153154

@@ -1574,7 +1575,8 @@ void PrintConfigDef::init_fff_params()
15741575
{ "adaptivecubic", L("Adaptive Cubic")},
15751576
{ "supportcubic", L("Support Cubic")},
15761577
{ "lightning", L("Lightning")},
1577-
{ "zigzag", L("Zig Zag")}
1578+
{ "zigzag", L("Zig Zag")},
1579+
{ "crosshatch", L("Cross Hatch")}
15781580
});
15791581
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars));
15801582

src/libslic3r/PrintConfig.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ enum InfillPattern : int {
105105
ipRectilinear, ipMonotonic, ipMonotonicLines, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
106106
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase,
107107
ipLightning,
108+
ipCrossHatch,
108109
ipEnsuring,
109110
ipZigZag,
110111
ipCount,

src/libslic3r/PrintObject.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,9 +2065,16 @@ void PrintObject::bridge_over_infill()
20652065
};
20662066

20672067
// LAMBDA do determine optimal bridging angle
2068-
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern) {
2068+
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern, double fill_angle) {
20692069
AABBTreeLines::LinesDistancer<Line> lines_tree(anchors);
20702070

2071+
// Check it the infill that require a fixed infill angle.
2072+
switch (dominant_pattern) {
2073+
case ip3DHoneycomb:
2074+
case ipCrossHatch: return (fill_angle + 45.0) * 2.0 * M_PI / 360.;
2075+
default: break;
2076+
}
2077+
20712078
std::map<double, int> counted_directions;
20722079
for (const Polygon &p : bridged_area) {
20732080
double acc_distance = 0;
@@ -2442,11 +2449,12 @@ void PrintObject::bridge_over_infill()
24422449
double bridging_angle = 0;
24432450
if (!anchors.empty()) {
24442451
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors),
2445-
candidate.region->region().config().fill_pattern.value);
2452+
candidate.region->region().config().fill_pattern.value,
2453+
candidate.region->region().config().fill_angle.value);
24462454
} else {
24472455
// use expansion boundaries as anchors.
24482456
// Also, use Infill pattern that is neutral for angle determination, since there are no infill lines.
2449-
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine);
2457+
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine, 0);
24502458
}
24512459

24522460
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());

0 commit comments

Comments
 (0)