mirror of
https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git
synced 2026-06-06 05:42:59 -04:00
1) New algorithm for connecting along the perimeters is now applied to Honeycomb, Hilbert and similar planar filling curves. 2) The old expensive path chaining is not applied if the new algorithm to connect along the perimeter lines is called afterwards.
209 lines
7.5 KiB
C++
209 lines
7.5 KiB
C++
#include "../ClipperUtils.hpp"
|
|
#include "../ShortestPath.hpp"
|
|
#include "../Surface.hpp"
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
#include "FillGyroid.hpp"
|
|
|
|
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() * floor(width / period));
|
|
points.pop_back();
|
|
|
|
int n = points.size();
|
|
do {
|
|
points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
|
|
} 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) = clamp(0., height, double(point(1)));
|
|
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(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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
float infill_angle = 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);
|
|
// 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)));
|
|
|
|
// generate pattern
|
|
Polylines polylines = make_gyroid_waves(
|
|
scale_(this->z),
|
|
density_adjusted,
|
|
this->spacing,
|
|
ceil(bb.size()(0) / distance) + 1.,
|
|
ceil(bb.size()(1) / distance) + 1.);
|
|
|
|
// shift the polyline to the grid origin
|
|
for (Polyline &pl : polylines)
|
|
pl.translate(bb.min);
|
|
|
|
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
|
|
|
if (! polylines.empty())
|
|
// remove too small bits (larger than longer)
|
|
polylines.erase(
|
|
//FIXME what is the small size? Removing tiny extrusions disconnects walls!
|
|
std::remove_if(polylines.begin(), polylines.end(), [this](const Polyline &pl) { return pl.length() < scale_(this->spacing * 3); }),
|
|
polylines.end());
|
|
|
|
if (! polylines.empty()) {
|
|
// connect lines
|
|
size_t polylines_out_first_idx = polylines_out.size();
|
|
if (params.dont_connect)
|
|
append(polylines_out, chain_polylines(polylines));
|
|
else
|
|
this->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
|