mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 09:11:08 +00:00
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
196 lines
8.8 KiB
C++
196 lines
8.8 KiB
C++
#include "../ClipperUtils.hpp"
|
|
#include "../MarchingSquares.hpp"
|
|
#include "libslic3r/Point.hpp"
|
|
#include "libslic3r/libslic3r.h"
|
|
#include "libslic3r/BoundingBox.hpp"
|
|
#include "libslic3r/Polyline.hpp"
|
|
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
|
#include "libslic3r/Fill/FillBase.hpp"
|
|
#include "libslic3r/ExPolygon.hpp"
|
|
#include "FillTpmsFK.hpp"
|
|
#include "FillTpmsAdaptive.hpp"
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <math.h>
|
|
#include <vector>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include "libslic3r/Polygon.hpp"
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
|
|
namespace Slic3r {
|
|
|
|
// Fischer - Koch S equation:
|
|
// cos(2x)sin(y)cos(z) + cos(2y)sin(z)cos(x) + cos(2z)sin(x)cos(y) = 0
|
|
static float fischer_koch(float x, float y, float z)
|
|
{
|
|
return cosf(2 * x) * sinf(y) * cosf(z) + cosf(2 * y) * sinf(z) * cosf(x) + cosf(2 * z) * sinf(x) * cosf(y);
|
|
}
|
|
|
|
} // namespace Slic3r
|
|
|
|
namespace marchsq {
|
|
using namespace Slic3r;
|
|
|
|
using coordr_t = long; // length type for (r, c) raster coordinates.
|
|
// Note that coordf_t, Pointfs, Point3f, etc all use double not float.
|
|
using Pointf = Vec2d; // (x, y) field point in coordf_t.
|
|
|
|
struct ScalarField
|
|
{
|
|
static constexpr float gsizef = 0.40; // grid cell size in mm (roughly line segment length).
|
|
static constexpr float rsizef = 0.004; // raster pixel size in mm (roughly point accuracy).
|
|
const coord_t rsize = scaled(rsizef); // raster pixel size in coord_t.
|
|
const coordr_t gsize = std::round(gsizef / rsizef); // grid cell size in coordr_t.
|
|
Point size; // field size in coord_t.
|
|
Point offs; // field offset in coord_t.
|
|
coordf_t z; // z offset as a float.
|
|
float freq; // field frequency in cycles per mm.
|
|
float isoval = 0.0; // iso value threshold to use.
|
|
|
|
explicit ScalarField(const BoundingBox bb, const coordf_t z = 0.0, const float period = 10.0)
|
|
: size{bb.size()}, offs{bb.min}, z{z}, freq{float(2 * PI) / period}
|
|
{}
|
|
|
|
// Get the scalar field value at x,y,z in coordf_t coordinates.
|
|
float get_scalar(coordf_t x, coordf_t y, coordf_t z) const { return fischer_koch(freq * x, freq * y, freq * z); }
|
|
|
|
// Get the scalar field value at a Coord for the current z value.
|
|
float get_scalar(Coord p) const
|
|
{
|
|
Pointf pf = to_Pointf(p);
|
|
return get_scalar(pf.x(), pf.y(), z);
|
|
}
|
|
|
|
// Convert between dimension scales.
|
|
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; }
|
|
|
|
// Convert between point/coordinate systems, including translation.
|
|
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)); }
|
|
};
|
|
|
|
// Register ScalarField as a RasterType for MarchingSquares.
|
|
template<> struct _RasterTraits<ScalarField>
|
|
{
|
|
// The type of pixel cell in the raster
|
|
using ValueType = float;
|
|
|
|
// Value at a given position
|
|
static float get(const ScalarField& sf, size_t row, size_t col) { return sf.get_scalar(Coord(row, col)); }
|
|
|
|
// Number of rows and cols of the raster
|
|
static size_t rows(const ScalarField& sf) { return sf.to_coordr(sf.size.y()); }
|
|
static size_t cols(const ScalarField& sf) { return sf.to_coordr(sf.size.x()); }
|
|
};
|
|
|
|
// Get the polylines for the scalar field. The tolerance is used for
|
|
// simplifying the polylines to remove redundant points. The default will
|
|
// only remove points on (almost) perfectly straight lines. Set to -1 to turn
|
|
// off simplifying entirely. Note tolerance is the max line deviation from
|
|
// simplifying and should be scaled.
|
|
Polylines get_polylines(const ScalarField& 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());
|
|
// size_t old_pts = 0, new_pts = 0;
|
|
|
|
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));
|
|
// MarchingSquare's rings are polygons, so add the first point to the end to make it a PolyLine.
|
|
pts.push_back(pts.front());
|
|
// old_pts += poly.points.size();
|
|
// Simplify within specified tolerance to reduce points.
|
|
if (tolerance >= 0.0)
|
|
poly.simplify(tolerance);
|
|
// new_pts += poly.points.size();
|
|
polys.emplace_back(poly);
|
|
}
|
|
// std::cerr << "MarchingSquares: poly.simplify(" << tolerance << ") reduced points from" <<
|
|
// old_pts << " to " << new_pts << " (" << 100*new_pts/old_pts << "%)\n";
|
|
return polys;
|
|
}
|
|
|
|
} // namespace marchsq
|
|
|
|
namespace Slic3r {
|
|
|
|
void FillTpmsFK::_fill_surface_single(const FillParams& params,
|
|
unsigned int thickness_layers,
|
|
const std::pair<float, Point>& direction,
|
|
ExPolygon expolygon,
|
|
Polylines& polylines_out)
|
|
{
|
|
if (params.tpms_adaptive == TpmsAdaptiveMode::SteppedShells && this->tpms_radial_field != nullptr) {
|
|
fill_tpms_shells(*this->tpms_radial_field, expolygon, this->z - 0.5 * params.layer_height, params, this->spacing,
|
|
[&](const FillParams &shell_params, const ExPolygon &shell) {
|
|
this->_fill_surface_single(shell_params, thickness_layers, direction, shell, polylines_out);
|
|
});
|
|
return;
|
|
}
|
|
|
|
auto infill_angle = float(this->angle + (CorrectionAngle * 2 * M_PI) / 360.);
|
|
if (std::abs(infill_angle) >= EPSILON)
|
|
expolygon.rotate(-infill_angle);
|
|
|
|
// Density (field period) adjusted to have a good %of weight.
|
|
auto period = [¶ms, this](float density) { return 4.18f * spacing * params.multiline / std::min(0.9f, density); };
|
|
|
|
BoundingBox bbox = expolygon.contour.bounding_box();
|
|
// Enlarge the bounding box by the multi-line width to avoid artifacts at the edges.
|
|
bbox.offset(scale_((params.multiline + 1) * spacing));
|
|
Polylines polylines;
|
|
if (params.tpms_adaptive != TpmsAdaptiveMode::Disabled && this->tpms_radial_field != nullptr) {
|
|
polylines = make_adaptive_tpms({fischer_koch, 2. * PI / period(params.density), 2. * PI / period(params.tpms_interior_density),
|
|
params.tpms_adaptive_gradient},
|
|
*this->tpms_radial_field, bbox, this->z, params.layer_height, spacing, infill_angle);
|
|
} else {
|
|
marchsq::ScalarField sf = marchsq::ScalarField(bbox, this->z, period(params.density));
|
|
// Get simplified lines using coarse tolerance of 0.1mm (this is infill).
|
|
polylines = marchsq::get_polylines(sf, SCALED_SPARSE_INFILL_RESOLUTION);
|
|
}
|
|
|
|
// Apply multiline offset if needed
|
|
multiline_fill(polylines, params, spacing);
|
|
|
|
// Prune the lines within the expolygon.
|
|
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);
|
|
// chain_infill not situable for this pattern due to internal "islands", this also affect performance a lot.
|
|
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
|