|
1 | 1 | #include "../ClipperUtils.hpp" |
| 2 | +#include "../MarchingSquares.hpp" |
2 | 3 | #include "../ShortestPath.hpp" |
3 | 4 | #include "../Surface.hpp" |
4 | 5 | #include <cmath> |
5 | 6 | #include <algorithm> |
6 | 7 | #include <iostream> |
7 | 8 |
|
| 9 | +#include "FillBase.hpp" |
8 | 10 | #include "FillGyroid.hpp" |
9 | 11 |
|
| 12 | +// --------------------------------------------------------------------------- |
| 13 | +// Marching-squares scalar field for the optimized gyroid branch. |
| 14 | +// |
| 15 | +// The gyroid scalar field is the standard implicit equation |
| 16 | +// F(x,y,z) = sin(fx*x)cos(fy*y) + sin(fy*y)cos(fz*z) + sin(fz*z)cos(fx*x) |
| 17 | +// Marching squares extracts the iso-zero contour, giving smoother transitions |
| 18 | +// between vertical and horizontal regimes than the analytical asin-based wave |
| 19 | +// generator. Setting fz = omega * baseline anisotropically tightens the wave |
| 20 | +// along the layer-stacking axis, shortening the effective vertical strand |
| 21 | +// length and improving column-buckling resistance under Z-axis compression. |
| 22 | +// --------------------------------------------------------------------------- |
| 23 | +namespace marchsq { |
| 24 | +using namespace Slic3r; |
| 25 | + |
| 26 | +using coordr_t = long; |
| 27 | +using Pointf = Vec2d; |
| 28 | + |
| 29 | +struct GyroidField |
| 30 | +{ |
| 31 | + static constexpr float gsizef = 0.40f; |
| 32 | + static constexpr float rsizef = 0.004f; |
| 33 | + const coord_t rsize = scaled(rsizef); |
| 34 | + const coordr_t gsize = std::round(gsizef / rsizef); |
| 35 | + Point size; |
| 36 | + Point offs; |
| 37 | + coordf_t z; |
| 38 | + float fx; |
| 39 | + float fy; |
| 40 | + float fz; |
| 41 | + float isoval = 0.0f; |
| 42 | + |
| 43 | + explicit GyroidField(const BoundingBox bb, const coordf_t z, const float period, const float omega = 1.0f) |
| 44 | + : size{bb.size()}, offs{bb.min}, z{z} |
| 45 | + { |
| 46 | + const float baseline = float(2.0 * PI) / std::max(period, 1e-3f); |
| 47 | + fx = baseline; |
| 48 | + fy = baseline; |
| 49 | + fz = omega * baseline; |
| 50 | + } |
| 51 | + |
| 52 | + float get_scalar(coordf_t x, coordf_t y, coordf_t z_arg) const |
| 53 | + { |
| 54 | + const float a = fx * float(x); |
| 55 | + const float b = fy * float(y); |
| 56 | + const float c = fz * float(z_arg); |
| 57 | + return std::sin(a) * std::cos(b) + std::sin(b) * std::cos(c) + std::sin(c) * std::cos(a); |
| 58 | + } |
| 59 | + |
| 60 | + float get_scalar(Coord p) const |
| 61 | + { |
| 62 | + Pointf pf = to_Pointf(p); |
| 63 | + return get_scalar(pf.x(), pf.y(), z); |
| 64 | + } |
| 65 | + |
| 66 | + inline coord_t to_coord (const coordr_t& x) const { return x * rsize; } |
| 67 | + inline coordr_t to_coordr(const coord_t& x) const { return x / rsize; } |
| 68 | + inline Point to_Point (const Coord& p) const { return Point(to_coord(p.c) + offs.x(), to_coord(p.r) + offs.y()); } |
| 69 | + inline Coord to_Coord (const Point& p) const { return Coord(to_coordr(p.y() - offs.y()), to_coordr(p.x() - offs.x())); } |
| 70 | + inline Pointf to_Pointf(const Point& p) const { return Pointf(unscaled(p.x()), unscaled(p.y())); } |
| 71 | + inline Pointf to_Pointf(const Coord& p) const { return to_Pointf(to_Point(p)); } |
| 72 | +}; |
| 73 | + |
| 74 | +template<> struct _RasterTraits<GyroidField> |
| 75 | +{ |
| 76 | + using ValueType = float; |
| 77 | + static float get (const GyroidField& sf, size_t row, size_t col) { return sf.get_scalar(Coord(row, col)); } |
| 78 | + static size_t rows(const GyroidField& sf) { return sf.to_coordr(sf.size.y()); } |
| 79 | + static size_t cols(const GyroidField& sf) { return sf.to_coordr(sf.size.x()); } |
| 80 | +}; |
| 81 | + |
| 82 | +inline Polylines get_gyroid_polylines(const GyroidField& sf, const double tolerance = SCALED_EPSILON) |
| 83 | +{ |
| 84 | + std::vector<Ring> rings = execute_with_policy(ex_tbb, sf, sf.isoval, {sf.gsize, sf.gsize}); |
| 85 | + Polylines polys; |
| 86 | + polys.reserve(rings.size()); |
| 87 | + for (const Ring& ring : rings) { |
| 88 | + Polyline poly; |
| 89 | + Points& pts = poly.points; |
| 90 | + pts.reserve(ring.size() + 1); |
| 91 | + for (const Coord& crd : ring) |
| 92 | + pts.emplace_back(sf.to_Point(crd)); |
| 93 | + pts.push_back(pts.front()); |
| 94 | + if (tolerance >= 0.0) |
| 95 | + poly.simplify(tolerance); |
| 96 | + polys.emplace_back(poly); |
| 97 | + } |
| 98 | + return polys; |
| 99 | +} |
| 100 | + |
| 101 | +} // namespace marchsq |
| 102 | + |
10 | 103 | namespace Slic3r { |
11 | 104 |
|
| 105 | +// --------------------------------------------------------------------------- |
| 106 | +// Z-buckling bias optimization: omega = sqrt(1 / density_adj) clamped [1, 2]. |
| 107 | +// At low density (long, slender vertical strands) omega is highest; at ~30%+ |
| 108 | +// density it clamps to 1.0 (no-op). When false, behavior is byte-identical |
| 109 | +// to the standard parametric gyroid path below. |
| 110 | +// --------------------------------------------------------------------------- |
| 111 | +static inline double compute_omega_factor(double density_adjusted, double line_spacing, double layer_height) |
| 112 | +{ |
| 113 | + double lh_ratio = (line_spacing > 0.) ? layer_height / line_spacing : 0.5; |
| 114 | + double correction = 1.0 / std::sqrt(1.0 + lh_ratio); |
| 115 | + double raw = std::sqrt(1.0 / std::max(density_adjusted, 0.1)) * correction; |
| 116 | + return std::clamp(raw, 1.0, 2.0); |
| 117 | +} |
| 118 | + |
12 | 119 | static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip) |
13 | 120 | { |
14 | 121 | if (vertical) { |
@@ -169,19 +276,33 @@ void FillGyroid::_fill_surface_single( |
169 | 276 | bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); |
170 | 277 |
|
171 | 278 | // 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); |
| 279 | + Polylines polylines; |
| 280 | + if (params.gyroid_optimized) { |
| 281 | + // Marching-squares path on the gyroid implicit field with anisotropic Z-axis. |
| 282 | + const double lh = (params.layer_height > 0.) ? double(params.layer_height) : double(this->spacing); |
| 283 | + const double omega = compute_omega_factor(density_adjusted, this->spacing * params.multiline, lh); |
| 284 | + const float density_factor = std::max(0.001f, float(params.density * DensityAdjust / params.multiline)); |
| 285 | + const float period = float(2.0 * M_PI) * float(this->spacing) / density_factor; |
| 286 | + |
| 287 | + marchsq::GyroidField sf(bb, this->z, period, float(omega)); |
| 288 | + polylines = marchsq::get_gyroid_polylines(sf, SCALED_SPARSE_INFILL_RESOLUTION); |
| 289 | + } else { |
| 290 | + polylines = make_gyroid_waves( |
| 291 | + scale_(this->z), |
| 292 | + density_adjusted, |
| 293 | + this->spacing, |
| 294 | + ceil(bb.size()(0) / distance) + 1., |
| 295 | + ceil(bb.size()(1) / distance) + 1.); |
| 296 | + |
| 297 | + // shift the parametric output to the grid origin; marching squares already |
| 298 | + // emits absolute coords via GyroidField::to_Point so it skips this. |
| 299 | + for (Polyline &pl : polylines) |
| 300 | + pl.translate(bb.min); |
| 301 | + } |
| 302 | + |
182 | 303 | // Apply multiline offset if needed |
183 | 304 | multiline_fill(polylines, params, spacing); |
184 | | - |
| 305 | + |
185 | 306 | polylines = intersection_pl(polylines, expolygon); |
186 | 307 |
|
187 | 308 | if (! polylines.empty()) { |
|
0 commit comments