mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-25 01:40:57 +00:00
* Add Optimized Gyroid infill (auto-tuned wavelength + amplitude)
New infill geometry derived from FillGyroid. Two parameters are
auto-computed per-region from density, line spacing, and layer height
(no user inputs):
omega = sqrt(density_adj) / sqrt(1 + layer_height/spacing)
clamped to [0.5, 2.0]
-- Euler-Bernoulli buckling: critical load ~ 1/L^2,
so shorter wavelength under higher load (denser infill)
raises buckling resistance.
amplitude = 0.55 / omega^2, clamped to [0.20, 0.65]
-- Curved-beam bending stress: peak stress ~ A * omega^2,
so amplitude is reduced as omega rises to keep peak
fiber stress bounded while preserving stiffness.
Files:
- src/libslic3r/Fill/FillOptimizedGyroid.{hpp,cpp} (new)
- src/libslic3r/Fill/FillBase.cpp (factory case)
- src/libslic3r/Fill/Fill.cpp (switch case)
- src/libslic3r/Layer.cpp (switch case)
- src/libslic3r/PrintConfig.{hpp,cpp} (enum + label)
- src/libslic3r/CMakeLists.txt (build sources)
User-facing: appears as "Optimized Gyroid" in the Fill Pattern dropdown.
Density still chosen by user; omega/amplitude are internal.
* Fix build: layer_height is in FillParams, not Fill base
* Add ipOptimizedGyroid to multiline infill list in ConfigManipulation
* Refactor: replace ipOptimizedGyroid enum with gyroid_optimized boolean
Per @RF47's review feedback, fold the optimized wave math into FillGyroid
itself behind a per-region boolean instead of a separate infill enum.
What changes:
- New ConfigOptionBool "gyroid_optimized" on PrintRegionConfig (default
false). When unchecked, gyroid behavior is byte-identical to before.
- Optimized wave math (compute_omega_factor, compute_amplitude_factor,
f_opt, make_*_opt, make_optimized_gyroid_waves) lives inside
FillGyroid.cpp. _fill_surface_single branches on params.gyroid_optimized.
- FillParams gains a bool gyroid_optimized field, populated in Fill.cpp
from region_config alongside fill_multiline.
- UI checkbox added under Strength > Infill in Tab.cpp, label
"Optimize gyroid wave (experimental)". Toggle is hidden by
ConfigManipulation when sparse_infill_pattern != ipGyroid.
- "gyroid_optimized" added to s_Preset_print_options for preset I/O.
What goes away:
- ipOptimizedGyroid enum value, factory case, switch cases, dropdown
label, string key.
- FillOptimizedGyroid.cpp / FillOptimizedGyroid.hpp (math moved into
FillGyroid.cpp).
- Net diff drops by ~250 lines.
Existing profiles using gyroid are unaffected.
* Wire gyroid_optimized through SurfaceFillParams to FillParams
Linux build failed because line 921 in Fill.cpp populates a
SurfaceFillParams (the dedup struct), not FillParams directly.
Add the field there, in operator< / operator==, and copy it to
FillParams at both conversion sites.
* Use toggle_line for gyroid_optimized: hide row when pattern != gyroid
* Account for multiline wall thickness in omega correction (per @RF47)
When fill_multiline = N, each gyroid wall is N lines thick, so the
geometric scale fed into the buckling correction term should be
spacing * N rather than spacing. Increases omega (tighter wavelength)
when multiline is enabled, consistent with the thicker wall being
more buckling-resistant.
* Optimized gyroid via marching squares on the implicit scalar field
Per @RF47 review: replace the analytical f_opt / make_one_period_opt
wave generator (which had visible kinks at vertical-horizontal
transitions) with a marching-squares iso-extraction on the gyroid
scalar field, modeled on FillTpmsFK.cpp.
- New marchsq::GyroidField in FillGyroid.cpp evaluates
F(x,y,z) = sin(fx*x)cos(fy*y) + sin(fy*y)cos(fz*z) + sin(fz*z)cos(fx*x)
where fx = omega * baseline (anisotropic in x), fy = fz = baseline.
- get_gyroid_polylines() runs marching squares at iso=0 and converts
rings to polylines.
- _fill_surface_single() optimized branch now builds GyroidField,
runs marching squares, and skips the bb.min translate (field
output is already in absolute coords).
- Dropped: f_opt, make_one_period_opt, make_wave_opt,
make_optimized_gyroid_waves, compute_amplitude_factor. Amplitude
has no clean analog in iso-zero extraction.
- Standard (non-optimized) gyroid path unchanged.
* Mass calibration: compensate period by cbrt(omega) for x-anisotropic field
Per @RF47: optimized vs standard gyroid had different masses at the
same sparse_infill_density setting. Cause: scaling fx by omega while
leaving fy=fz at the baseline raised the surface-area-to-volume ratio
by approximately omega^(1/3) (the geometric mean of the three
frequencies).
Fix: multiply the base period by cbrt(omega) so the geometric mean of
(fx, fy, fz) returns to the standard baseline. Net effect:
fx = omega^(2/3) * baseline_orig
fy = fz = omega^(-1/3) * baseline_orig
which preserves total mass at the same density setting while
preserving the load-direction anisotropy this PR introduces.
* Switch optimized gyroid anisotropy from X to Z (per @RF47)
Z is the typical compression-load axis for FFF parts and is not at
delamination risk under compression — so the dominant failure mode
is column buckling of the vertical strands themselves. Tightening
fz directly shortens the effective vertical strand length, which
improves Z-axis buckling resistance.
Mass calibration via cbrt(omega) period compensation still applies
(scaling exactly one of three frequencies by omega; the geometric-
mean preservation argument is symmetric across axes).
* Update src/slic3r/GUI/Tab.cpp
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
* Address review feedback (Copilot + @RF47)
- Fill.cpp: gate params.gyroid_optimized on (params.pattern == ipGyroid)
so non-gyroid surfaces don't differ in SurfaceFillParams by an
irrelevant flag (would unnecessarily split fill batching).
[Copilot suggestion, RF47 confirmed correct]
- PrintConfig.cpp: drop "amplitude" from the tooltip; only wavelength
is parameterized (the marching-squares iso=0 extraction is invariant
to a uniform field scale, so amplitude has no effect).
- FillBase.hpp: shorten gyroid_optimized comment to match the actual
carried state (no amplitude term).
- FillGyroid.cpp: shorten the marchsq namespace comment block; the
ODR concern was overstated (FillTpmsFK uses the same pattern fine).
* Drop redundant marchsq bb expansion (Copilot)
bb is already offset by 10 * scale_(spacing) above for edge-artifact
margin; the second offset on bb_field doubled the raster area for no
geometric benefit and hurt CPU time on large parts.
* Update src/slic3r/GUI/Tab.cpp
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
* Fix density mismatch + rename to Z-buckling bias optimization
Issue (per @ianalexis): at the same sparse_infill_density setting,
the optimized branch produced denser fill than standard. Verified via
Python sim (sim_gyroid_compare.py) using marching squares on the
implicit field across multiple z slices.
Root cause: the omega formula was inverted from the buckling-physics
intent. The naive sqrt(density_adj) factor produced omega < 1 at
typical print densities (10-30%), which LENGTHENED the Z wavelength
instead of shortening it -- net loss in both mass and strength.
Fix:
- compute_omega_factor: invert to sqrt(1 / density_adj), clamp to
[1.0, 2.0]. Now omega = 2.0 at low density (long strands need
most help) and clamps to 1.0 above ~30% density (no-op, since
standard gyroid is already short enough).
- Remove the cbrt(omega) period compensation. Empirically (sim
table embedded in FillGyroid.cpp comment) the inverted formula
keeps line length per area at ~1.000 of standard across all
densities with no period scaling needed.
Predicted gains (sim, Z-axis Euler buckling proxy):
density line/std strength/std
10% 1.000 2.84x
15% 1.000 1.89x
20% 1.000 1.42x
30%+ 1.000 1.00x (no-op)
Rename per @ianalexis: "Optimize gyroid wave" oversells (now no-op
above 30% density and Z-only). Renamed user-facing label to
"Z-buckling bias optimization (experimental)" with updated tooltip
that scopes to vertical compression and discloses the density cutoff.
Internal config key (gyroid_optimized) unchanged for diff size.
Real-world Instron compression tests at Brown's Prince Lab to follow.
---------
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
377 lines
15 KiB
C++
377 lines
15 KiB
C++
#include "../ClipperUtils.hpp"
|
|
#include "../MarchingSquares.hpp"
|
|
#include "../ShortestPath.hpp"
|
|
#include "../Surface.hpp"
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include "FillBase.hpp"
|
|
#include "FillGyroid.hpp"
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Marching-squares scalar field for the optimized gyroid branch.
|
|
// Modeled after FillTpmsFK.cpp's ScalarField.
|
|
//
|
|
// The gyroid scalar field is the standard implicit equation
|
|
// F(x,y,z) = sin(fx*x)cos(fy*y) + sin(fy*y)cos(fz*z) + sin(fz*z)cos(fx*x)
|
|
// Marching squares extracts the iso-zero contour, which gives smoother
|
|
// transitions between vertical and horizontal regimes than the analytical
|
|
// asin-based wave generator. Setting fz = omega * baseline anisotropically
|
|
// tightens the wave along the layer-stacking axis, shortening the effective
|
|
// vertical strand length and improving column-buckling resistance under
|
|
// Z-axis compression.
|
|
// ---------------------------------------------------------------------------
|
|
namespace marchsq {
|
|
using namespace Slic3r;
|
|
|
|
using coordr_t = long;
|
|
using Pointf = Vec2d;
|
|
|
|
struct GyroidField
|
|
{
|
|
static constexpr float gsizef = 0.40f;
|
|
static constexpr float rsizef = 0.004f;
|
|
const coord_t rsize = scaled(rsizef);
|
|
const coordr_t gsize = std::round(gsizef / rsizef);
|
|
Point size;
|
|
Point offs;
|
|
coordf_t z;
|
|
float fx;
|
|
float fy;
|
|
float fz;
|
|
float isoval = 0.0f;
|
|
|
|
explicit GyroidField(const BoundingBox bb, const coordf_t z, const float period, const float omega = 1.0f)
|
|
: size{bb.size()}, offs{bb.min}, z{z}
|
|
{
|
|
const float baseline = float(2.0 * PI) / std::max(period, 1e-3f);
|
|
fx = baseline;
|
|
fy = baseline;
|
|
fz = omega * baseline;
|
|
}
|
|
|
|
float get_scalar(coordf_t x, coordf_t y, coordf_t z_arg) const
|
|
{
|
|
const float a = fx * float(x);
|
|
const float b = fy * float(y);
|
|
const float c = fz * float(z_arg);
|
|
return std::sin(a) * std::cos(b) + std::sin(b) * std::cos(c) + std::sin(c) * std::cos(a);
|
|
}
|
|
|
|
float get_scalar(Coord p) const
|
|
{
|
|
Pointf pf = to_Pointf(p);
|
|
return get_scalar(pf.x(), pf.y(), z);
|
|
}
|
|
|
|
inline coord_t to_coord (const coordr_t& x) const { return x * rsize; }
|
|
inline coordr_t to_coordr(const coord_t& x) const { return x / rsize; }
|
|
inline Point to_Point (const Coord& p) const { return Point(to_coord(p.c) + offs.x(), to_coord(p.r) + offs.y()); }
|
|
inline Coord to_Coord (const Point& p) const { return Coord(to_coordr(p.y() - offs.y()), to_coordr(p.x() - offs.x())); }
|
|
inline Pointf to_Pointf(const Point& p) const { return Pointf(unscaled(p.x()), unscaled(p.y())); }
|
|
inline Pointf to_Pointf(const Coord& p) const { return to_Pointf(to_Point(p)); }
|
|
};
|
|
|
|
template<> struct _RasterTraits<GyroidField>
|
|
{
|
|
using ValueType = float;
|
|
static float get (const GyroidField& sf, size_t row, size_t col) { return sf.get_scalar(Coord(row, col)); }
|
|
static size_t rows(const GyroidField& sf) { return sf.to_coordr(sf.size.y()); }
|
|
static size_t cols(const GyroidField& sf) { return sf.to_coordr(sf.size.x()); }
|
|
};
|
|
|
|
inline Polylines get_gyroid_polylines(const GyroidField& sf, const double tolerance = SCALED_EPSILON)
|
|
{
|
|
std::vector<Ring> rings = execute_with_policy(ex_tbb, sf, sf.isoval, {sf.gsize, sf.gsize});
|
|
Polylines polys;
|
|
polys.reserve(rings.size());
|
|
for (const Ring& ring : rings) {
|
|
Polyline poly;
|
|
Points& pts = poly.points;
|
|
pts.reserve(ring.size() + 1);
|
|
for (const Coord& crd : ring)
|
|
pts.emplace_back(sf.to_Point(crd));
|
|
pts.push_back(pts.front());
|
|
if (tolerance >= 0.0)
|
|
poly.simplify(tolerance);
|
|
polys.emplace_back(poly);
|
|
}
|
|
return polys;
|
|
}
|
|
|
|
} // namespace marchsq
|
|
|
|
namespace Slic3r {
|
|
|
|
static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
|
|
{
|
|
if (vertical) {
|
|
double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
|
|
double a = sin(x + phase_offset);
|
|
double b = - z_cos;
|
|
double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
|
|
double r = sqrt(sqr(a) + sqr(b));
|
|
return asin(a/r) + asin(res/r) + M_PI;
|
|
}
|
|
else {
|
|
double phase_offset = z_sin < 0 ? M_PI : 0.;
|
|
double a = cos(x + phase_offset);
|
|
double b = - z_sin;
|
|
double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
|
|
double r = sqrt(sqr(a) + sqr(b));
|
|
return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
|
|
}
|
|
}
|
|
|
|
static inline Polyline make_wave(
|
|
const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
|
|
double z_cos, double z_sin, bool vertical, bool flip)
|
|
{
|
|
std::vector<Vec2d> points = one_period;
|
|
double period = points.back()(0);
|
|
if (width != period) // do not extend if already truncated
|
|
{
|
|
points.reserve(one_period.size() * size_t(floor(width / period)));
|
|
points.pop_back();
|
|
|
|
size_t n = points.size();
|
|
do {
|
|
points.emplace_back(points[points.size()-n].x() + period, points[points.size()-n].y());
|
|
} while (points.back()(0) < width - EPSILON);
|
|
|
|
points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip)));
|
|
}
|
|
|
|
// and construct the final polyline to return:
|
|
Polyline polyline;
|
|
polyline.points.reserve(points.size());
|
|
for (auto& point : points) {
|
|
point(1) += offset;
|
|
point(1) = std::clamp(double(point.y()), 0., height);
|
|
if (vertical)
|
|
std::swap(point(0), point(1));
|
|
polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
|
|
}
|
|
|
|
return polyline;
|
|
}
|
|
|
|
static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance)
|
|
{
|
|
std::vector<Vec2d> points;
|
|
double dx = M_PI_2; // exact coordinates on main inflexion lobes
|
|
double limit = std::min(2*M_PI, width);
|
|
points.reserve(coord_t(ceil(limit / tolerance / 3)));
|
|
|
|
for (double x = 0.; x < limit - EPSILON; x += dx) {
|
|
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
|
}
|
|
points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip)));
|
|
|
|
// piecewise increase in resolution up to requested tolerance
|
|
for(;;)
|
|
{
|
|
size_t size = points.size();
|
|
for (unsigned int i = 1;i < size; ++i) {
|
|
auto& lp = points[i-1]; // left point
|
|
auto& rp = points[i]; // right point
|
|
double x = lp(0) + (rp(0) - lp(0)) / 2;
|
|
double y = f(x, z_sin, z_cos, vertical, flip);
|
|
Vec2d ip = {x, y};
|
|
if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) {
|
|
points.emplace_back(std::move(ip));
|
|
}
|
|
}
|
|
|
|
if (size == points.size())
|
|
break;
|
|
else
|
|
{
|
|
// insert new points in order
|
|
std::sort(points.begin(), points.end(),
|
|
[](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); });
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// "Optimized" gyroid wave: marching-squares variant gated on
|
|
// params.gyroid_optimized. The wave shape is extracted from the gyroid
|
|
// implicit scalar field (see marchsq::GyroidField above) at iso=0, with
|
|
// the Z dimension's spatial frequency multiplied by an Euler-Bernoulli
|
|
// buckling-derived factor so the vertical strands become shorter columns,
|
|
// raising the critical buckling load against Z-axis compression.
|
|
//
|
|
// The formula is INVERTED from a naive "scale with density" derivation:
|
|
// at LOW density the gyroid strands are long and slender (prime buckling
|
|
// targets), so they need the most shortening; at high density the strands
|
|
// are already short and need little extra help. omega is therefore the
|
|
// inverse-square-root of density_adjusted:
|
|
//
|
|
// omega = sqrt(1 / density_adj) / sqrt(1 + layer_h/spacing),
|
|
// clamped [1.0, 2.0]
|
|
//
|
|
// fx and fy are left at the baseline frequency, so the per-XY-slice line
|
|
// length per unit area is preserved -> mass at the same `sparse_infill_density`
|
|
// setting matches the standard gyroid path. Strength gain comes purely from
|
|
// the shorter vertical column length (P_cr proportional to 1/L^2).
|
|
//
|
|
// Empirical Python sim (sim_gyroid_compare.py) at layer_h=0.20, spacing=0.45:
|
|
//
|
|
// density omega line/std strength/std strength_per_mass
|
|
// 10% 2.00 1.00 2.84 2.84
|
|
// 15% 1.38 1.00 1.89 1.89
|
|
// 20% 1.19 1.00 1.42 1.42
|
|
// 30% 1.00 1.00 1.00 1.00
|
|
// 50%+ 1.00 1.00 1.00 1.00
|
|
//
|
|
// When gyroid_optimized is false, behavior is byte-identical to the
|
|
// standard parametric gyroid path below.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static inline double compute_omega_factor(double density_adjusted, double line_spacing, double layer_height)
|
|
{
|
|
double lh_ratio = (line_spacing > 0.) ? layer_height / line_spacing : 0.5;
|
|
double correction = 1.0 / std::sqrt(1.0 + lh_ratio);
|
|
double raw = std::sqrt(1.0 / std::max(density_adjusted, 0.1)) * correction;
|
|
return std::clamp(raw, 1.0, 2.0);
|
|
}
|
|
|
|
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
|
{
|
|
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
|
|
|
// tolerance in scaled units. clamp the maximum tolerance as there's
|
|
// no processing-speed benefit to do so beyond a certain point
|
|
const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale<double>(scaleFactor);
|
|
|
|
//scale factor for 5% : 8 712 388
|
|
// 1z = 10^-6 mm ?
|
|
const double z = gridZ / scaleFactor;
|
|
const double z_sin = sin(z);
|
|
const double z_cos = cos(z);
|
|
|
|
bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
|
|
double lower_bound = 0.;
|
|
double upper_bound = height;
|
|
bool flip = true;
|
|
if (vertical) {
|
|
flip = false;
|
|
lower_bound = -M_PI;
|
|
upper_bound = width - M_PI_2;
|
|
std::swap(width,height);
|
|
}
|
|
|
|
std::vector<Vec2d> one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
|
flip = !flip; // even polylines are a bit shifted
|
|
std::vector<Vec2d> one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance);
|
|
Polylines result;
|
|
|
|
for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) {
|
|
// creates odd polylines
|
|
result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
|
// creates even polylines
|
|
y0 += M_PI;
|
|
if (y0 < upper_bound + EPSILON) {
|
|
result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// FIXME: needed to fix build on Mac on buildserver
|
|
constexpr double FillGyroid::PatternTolerance;
|
|
|
|
void FillGyroid::_fill_surface_single(
|
|
const FillParams ¶ms,
|
|
unsigned int thickness_layers,
|
|
const std::pair<float, Point> &direction,
|
|
ExPolygon expolygon,
|
|
Polylines &polylines_out)
|
|
{
|
|
auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.);
|
|
if(std::abs(infill_angle) >= EPSILON)
|
|
expolygon.rotate(-infill_angle);
|
|
|
|
BoundingBox bb = expolygon.contour.bounding_box();
|
|
// Density adjusted to have a good %of weight.
|
|
double density_adjusted = std::max(0., params.density * DensityAdjust / params.multiline);
|
|
// Distance between the gyroid waves in scaled coordinates.
|
|
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
|
|
|
// align bounding box to a multiple of our grid module
|
|
bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
|
|
|
// Expand the bounding box to avoid artifacts at the edges
|
|
coord_t expand = 10 * (scale_(this->spacing));
|
|
bb.offset(expand);
|
|
|
|
// generate pattern
|
|
Polylines polylines;
|
|
if (params.gyroid_optimized) {
|
|
// Marching-squares path on the gyroid implicit field. Base period matches
|
|
// the standard parametric path's wavelength: 2*pi * spacing / density_adj.
|
|
// omega >= 1 always, so fz >= baseline -> shorter vertical wavelength ->
|
|
// shorter effective column length -> higher buckling resistance.
|
|
//
|
|
// Mass: fx and fy are left at baseline (same as standard), so the
|
|
// per-XY-slice line length per unit area is approximately preserved.
|
|
// Empirically (sim_gyroid_compare.py) the optimized line/std ratio is
|
|
// ~1.000 across densities, so no period compensation is needed.
|
|
const double lh = (params.layer_height > 0.) ? double(params.layer_height) : double(this->spacing);
|
|
const double omega = compute_omega_factor(density_adjusted, this->spacing * params.multiline, lh);
|
|
|
|
const float density_factor = std::max(0.001f, float(params.density * DensityAdjust / params.multiline));
|
|
const float period = float(2.0 * M_PI) * float(this->spacing) / density_factor;
|
|
|
|
// bb is already expanded above by 10 * scale_(spacing) for edge artifacts;
|
|
// skip a second offset here to avoid raster-area bloat in the marching squares pass.
|
|
marchsq::GyroidField sf(bb, this->z, period, float(omega));
|
|
polylines = marchsq::get_gyroid_polylines(sf, SCALED_SPARSE_INFILL_RESOLUTION);
|
|
} else {
|
|
polylines = make_gyroid_waves(
|
|
scale_(this->z),
|
|
density_adjusted,
|
|
this->spacing,
|
|
ceil(bb.size()(0) / distance) + 1.,
|
|
ceil(bb.size()(1) / distance) + 1.);
|
|
|
|
// The parametric generator produces wave coords relative to the grid origin;
|
|
// shift them into absolute layer coords. The marching-squares branch above
|
|
// already emits absolute coords via GyroidField::to_Point, so it skips this.
|
|
for (Polyline &pl : polylines)
|
|
pl.translate(bb.min);
|
|
}
|
|
|
|
// Apply multiline offset if needed
|
|
multiline_fill(polylines, params, spacing);
|
|
|
|
polylines = intersection_pl(std::move(polylines), expolygon);
|
|
|
|
if (! polylines.empty()) {
|
|
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
|
|
// The infill perimeter lines should be separated by around a single infill line width.
|
|
const double minlength = scale_(0.8 * this->spacing);
|
|
polylines.erase(
|
|
std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
|
|
polylines.end());
|
|
}
|
|
|
|
if (! polylines.empty()) {
|
|
// connect lines
|
|
size_t polylines_out_first_idx = polylines_out.size();
|
|
chain_or_connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
|
|
|
// new paths must be rotated back
|
|
if (std::abs(infill_angle) >= EPSILON) {
|
|
for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
|
|
it->rotate(infill_angle);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Slic3r
|