mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
* Add Missing Includes Across src/libslic3r Every libslic3r source and header now directly includes the headers declaring what it uses, rather than relying on the precompiled header or transitive includes. Generated with clang-tidy misc-include-cleaner, with libslic3r headers spelled libslic3r/... so they resolve outside the library's private include paths. MultiMaterialSegmentation.hpp, Support/SupportParameters.hpp and Format/STEP.hpp are made self-contained by hand. * Make the libslic3r Headers Compile on Their Own Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Left out: I18N.hpp, which errors on purpose when included from GUI code, and VoxelizeCSGMesh.hpp and SLA/bicubic.h, which nothing includes and which no longer compile at all. * Add the Includes Missing From the Hand-Fixed libslic3r Headers clang-tidy would not edit these headers while they failed to compile on their own, so the first pass skipped them. With the headers now self-contained, a second pass adds the rest. * Keep Windows Setup Ahead of the Added libslic3r Includes Print.cpp and Thread.cpp open with a _WIN32 block that has to come first; without the precompiled header, Print.cpp otherwise reaches windows.h through OCCT with NONLS defined and boost/regex fails. OpenVDBUtils.cpp and SLA/SupportTreeBuilder.cpp had includes inside #ifndef NOMINMAX, which libslic3r defines on Windows, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory. * Re-Add libslic3r Includes After the Clipper2 2.0.1 Migration Rebasing onto main took main's version of the files the Clipper2 migration rewrote, so their added includes are restored here, along with includes for main's new code. Clipper2's individual headers are now ignored by clang-tidy: they only build the Z variant through clipper2_z.hpp, which defines USINGZ first, so including clipper.core.h and the like directly broke ClipperZUtils.cpp.
322 lines
12 KiB
C++
322 lines
12 KiB
C++
#include "../ClipperUtils.hpp"
|
|
#include "../ShortestPath.hpp"
|
|
#include "../Surface.hpp"
|
|
#include "libslic3r/ExPolygon.hpp"
|
|
#include "libslic3r/BoundingBox.hpp"
|
|
#include "FillBase.hpp"
|
|
#include "FillCornerSmoothing.hpp"
|
|
#include "libslic3r/libslic3r.h"
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include <cstddef>
|
|
#include "libslic3r/Point.hpp"
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include "libslic3r/Polyline.hpp"
|
|
#include <utility>
|
|
#include "Fill3DHoneycomb.hpp"
|
|
|
|
namespace Slic3r {
|
|
|
|
// sign function
|
|
template <typename T> int sgn(T val) {
|
|
return (T(0) < val) - (val < T(0));
|
|
}
|
|
|
|
/*
|
|
Creates a contiguous sequence of points at a specified height that make
|
|
up a horizontal slice of the edges of a space filling truncated
|
|
octahedron tesselation. The octahedrons are oriented so that the
|
|
square faces are in the horizontal plane with edges parallel to the X
|
|
and Y axes.
|
|
|
|
Credits: David Eccles (gringer).
|
|
*/
|
|
|
|
// triangular wave function
|
|
// this has period (gridSize * 2), and amplitude (gridSize / 2),
|
|
// with triWave(pos = 0) = 0
|
|
static coordf_t triWave(coordf_t pos, coordf_t gridSize)
|
|
{
|
|
float t = (pos / (gridSize * 2.)) + 0.25; // convert relative to grid size
|
|
t = t - (int)t; // extract fractional part
|
|
return((1. - abs(t * 8. - 4.)) * (gridSize / 4.) + (gridSize / 4.));
|
|
}
|
|
|
|
// truncated octagonal waveform, with period and offset
|
|
// as per the triangular wave function. The Z position adjusts
|
|
// the maximum offset [between -(gridSize / 4) and (gridSize / 4)], with a
|
|
// period of (gridSize * 2) and troctWave(Zpos = 0) = 0
|
|
static coordf_t troctWave(coordf_t pos, coordf_t gridSize, coordf_t Zpos)
|
|
{
|
|
coordf_t Zcycle = triWave(Zpos, gridSize);
|
|
coordf_t perpOffset = Zcycle / 2;
|
|
coordf_t y = triWave(pos, gridSize);
|
|
return((abs(y) > abs(perpOffset)) ?
|
|
(sgn(y) * perpOffset) :
|
|
(y * sgn(perpOffset)));
|
|
}
|
|
|
|
// Identify the important points of curve change within a truncated
|
|
// octahedron wave (as waveform fraction t):
|
|
// 1. Start of wave (always 0.0)
|
|
// 2. Transition to upper "horizontal" part
|
|
// 3. Transition from upper "horizontal" part
|
|
// 4. Transition to lower "horizontal" part
|
|
// 5. Transition from lower "horizontal" part
|
|
/* o---o
|
|
* / \
|
|
* o/ \
|
|
* \ /
|
|
* \ /
|
|
* o---o
|
|
*/
|
|
static std::vector<coordf_t> getCriticalPoints(coordf_t Zpos, coordf_t gridSize)
|
|
{
|
|
std::vector<coordf_t> res = {0.};
|
|
coordf_t perpOffset = abs(triWave(Zpos, gridSize) / 2.);
|
|
|
|
coordf_t normalisedOffset = perpOffset / gridSize;
|
|
// // for debugging: just generate evenly-distributed points
|
|
// for(coordf_t i = 0; i < 2; i += 0.05){
|
|
// res.push_back(gridSize * i);
|
|
// }
|
|
// note: 0 == straight line
|
|
if(normalisedOffset > 0){
|
|
res.push_back(gridSize * (0. + normalisedOffset));
|
|
res.push_back(gridSize * (1. - normalisedOffset));
|
|
res.push_back(gridSize * (1. + normalisedOffset));
|
|
res.push_back(gridSize * (2. - normalisedOffset));
|
|
}
|
|
return(res);
|
|
}
|
|
|
|
// Generate an array of points that are in the same direction as the
|
|
// basic printing line (i.e. Y points for columns, X points for rows)
|
|
// Note: a negative offset only causes a change in the perpendicular
|
|
// direction
|
|
static std::vector<coordf_t> colinearPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
|
|
const size_t baseLocation, size_t gridLength)
|
|
{
|
|
std::vector<coordf_t> points;
|
|
points.push_back(baseLocation);
|
|
for (coordf_t cLoc = baseLocation; cLoc < gridLength; cLoc+= (gridSize*2)) {
|
|
for(size_t pi = 0; pi < critPoints.size(); pi++){
|
|
points.push_back(baseLocation + cLoc + critPoints[pi]);
|
|
}
|
|
}
|
|
points.push_back(gridLength);
|
|
return points;
|
|
}
|
|
|
|
// Generate an array of points for the dimension that is perpendicular to
|
|
// the basic printing line (i.e. X points for columns, Y points for rows)
|
|
static std::vector<coordf_t> perpendPoints(const coordf_t Zpos, coordf_t gridSize, std::vector<coordf_t> critPoints,
|
|
size_t baseLocation, size_t gridLength,
|
|
size_t offsetBase, coordf_t perpDir)
|
|
{
|
|
std::vector<coordf_t> points;
|
|
points.push_back(offsetBase);
|
|
for (coordf_t cLoc = baseLocation; cLoc < gridLength; cLoc+= gridSize*2) {
|
|
for(size_t pi = 0; pi < critPoints.size(); pi++){
|
|
coordf_t offset = troctWave(critPoints[pi], gridSize, Zpos);
|
|
points.push_back(offsetBase + (offset * perpDir));
|
|
}
|
|
}
|
|
points.push_back(offsetBase);
|
|
return points;
|
|
}
|
|
|
|
static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coordf_t> &y)
|
|
{
|
|
assert(x.size() == y.size());
|
|
Pointfs out;
|
|
out.reserve(x.size());
|
|
for (size_t i = 0; i < x.size(); ++ i)
|
|
out.push_back(Vec2d(x[i], y[i]));
|
|
return out;
|
|
}
|
|
|
|
// Generate a set of curves (array of array of 2d points) that describe a
|
|
// horizontal slice of a truncated regular octahedron.
|
|
static std::vector<Pointfs> makeActualGrid(coordf_t Zpos, coordf_t gridSize, size_t boundsX, size_t boundsY)
|
|
{
|
|
std::vector<Pointfs> points;
|
|
std::vector<coordf_t> critPoints = getCriticalPoints(Zpos, gridSize);
|
|
coordf_t zCycle = fmod(Zpos + gridSize/2, gridSize * 2.) / (gridSize * 2.);
|
|
bool printVert = zCycle < 0.5;
|
|
if (printVert) {
|
|
int perpDir = -1;
|
|
for (coordf_t x = 0; x <= (boundsX); x+= gridSize, perpDir *= -1) {
|
|
points.push_back(Pointfs());
|
|
Pointfs &newPoints = points.back();
|
|
newPoints = zip(
|
|
perpendPoints(Zpos, gridSize, critPoints, 0, boundsY, x, perpDir),
|
|
colinearPoints(Zpos, gridSize, critPoints, 0, boundsY));
|
|
if (perpDir == 1)
|
|
std::reverse(newPoints.begin(), newPoints.end());
|
|
}
|
|
} else {
|
|
int perpDir = 1;
|
|
for (coordf_t y = gridSize; y <= (boundsY); y+= gridSize, perpDir *= -1) {
|
|
points.push_back(Pointfs());
|
|
Pointfs &newPoints = points.back();
|
|
newPoints = zip(
|
|
colinearPoints(Zpos, gridSize, critPoints, 0, boundsX),
|
|
perpendPoints(Zpos, gridSize, critPoints, 0, boundsX, y, perpDir));
|
|
if (perpDir == -1)
|
|
std::reverse(newPoints.begin(), newPoints.end());
|
|
}
|
|
}
|
|
return points;
|
|
}
|
|
|
|
// Generate a set of curves (array of array of 2d points) that describe a
|
|
// horizontal slice of a truncated regular octahedron with a specified
|
|
// grid square size.
|
|
// gridWidth and gridHeight define the width and height of the bounding box respectively
|
|
static Polylines makeGrid(coordf_t z, coordf_t gridSize, coordf_t boundWidth, coordf_t boundHeight, bool fillEvenly)
|
|
{
|
|
std::vector<Pointfs> polylines = makeActualGrid(z, gridSize, boundWidth, boundHeight);
|
|
Polylines result;
|
|
result.reserve(polylines.size());
|
|
for (std::vector<Pointfs>::const_iterator it_polylines = polylines.begin();
|
|
it_polylines != polylines.end(); ++ it_polylines) {
|
|
result.push_back(Polyline());
|
|
Polyline &polyline = result.back();
|
|
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
|
|
polyline.points.push_back(Point(coord_t((*it)(0)), coord_t((*it)(1))));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// FillParams has the following useful information:
|
|
// density <0 .. 1> [proportion of space to fill]
|
|
// anchor_length [???]
|
|
// anchor_length_max [???]
|
|
// dont_connect() [avoid connect lines]
|
|
// dont_adjust [avoid filling space evenly]
|
|
// monotonic [fill strictly left to right]
|
|
// complete [complete each loop]
|
|
|
|
void Fill3DHoneycomb::_fill_surface_single(
|
|
const FillParams ¶ms,
|
|
unsigned int thickness_layers,
|
|
const std::pair<float, Point> &direction,
|
|
ExPolygon expolygon,
|
|
Polylines &polylines_out)
|
|
{
|
|
// no rotation is supported for this infill pattern
|
|
// Support infill angle
|
|
auto infill_angle = float(this->angle);
|
|
if (std::abs(infill_angle) >= EPSILON) expolygon.rotate(-infill_angle);
|
|
BoundingBox bb = expolygon.contour.bounding_box();
|
|
|
|
// Expand the bounding box to avoid artifacts at the edges
|
|
coord_t expand = 5 * (scale_(this->spacing));
|
|
bb.offset(expand);
|
|
|
|
// Note: with equally-scaled X/Y/Z, the pattern will create a vertically-stretched
|
|
// truncated octahedron; so Z is pre-adjusted first by scaling by sqrt(2)
|
|
coordf_t zScale = sqrt(2);
|
|
|
|
// adjustment to account for the additional distance of octagram curves
|
|
// note: this only strictly applies for a rectangular area where the total
|
|
// Z travel distance is a multiple of the spacing... but it should
|
|
// be at least better than the prevous estimate which assumed straight
|
|
// lines
|
|
// = 4 * integrate(func=4*x(sqrt(2) - 1) + 1, from=0, to=0.25)
|
|
// = (sqrt(2) + 1) / 2 [... I think]
|
|
// make a first guess at the preferred grid Size
|
|
coordf_t gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density);
|
|
|
|
// This density calculation is incorrect for many values > 25%, possibly
|
|
// due to quantisation error, so this value is used as a first guess, then the
|
|
// Z scale is adjusted to make the layer patterns consistent / symmetric
|
|
// This means that the resultant infill won't be an ideal truncated octahedron,
|
|
// but it should look better than the equivalent quantised version
|
|
|
|
//Orca: uses a fixed layer height to avoid inconsistent bridges and variable layer height artifacts.
|
|
//coordf_t layerHeight = scale_(thickness_layers);
|
|
coordf_t layerHeight = scale_(1.0);
|
|
// ceiling to an integer value of layers per Z
|
|
// (with a little nudge in case it's close to perfect)
|
|
coordf_t layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
|
|
if(params.density > 0.42){ // exact layer pattern for >42% density
|
|
layersPerModule = 2;
|
|
// re-adjust the grid size for a partial octahedral path
|
|
// (scale of 1.1 guessed based on modeling)
|
|
gridSize = (scale_(this->spacing) * 1.1 * params.multiline / params.density);
|
|
// re-adjust zScale to make layering consistent
|
|
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
|
|
} else {
|
|
if(layersPerModule < 2){
|
|
layersPerModule = 2;
|
|
}
|
|
// re-adjust zScale to make layering consistent
|
|
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
|
|
// re-adjust the grid size to account for the new zScale
|
|
gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density);
|
|
// re-calculate layersPerModule and zScale
|
|
layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05);
|
|
if(layersPerModule < 2){
|
|
layersPerModule = 2;
|
|
}
|
|
zScale = (gridSize * 2) / (layersPerModule * layerHeight);
|
|
}
|
|
|
|
// align bounding box to a multiple of our honeycomb grid module
|
|
// (a module is 2*$gridSize since one $gridSize half-module is
|
|
// growing while the other $gridSize half-module is shrinking)
|
|
bb.merge(align_to_grid(bb.min, Point(gridSize*4, gridSize*4)));
|
|
|
|
// generate pattern
|
|
Polylines polylines =
|
|
makeGrid(
|
|
scale_(this->z) * zScale,
|
|
gridSize,
|
|
bb.size()(0),
|
|
bb.size()(1),
|
|
!params.dont_adjust);
|
|
|
|
// move pattern in place
|
|
for (Polyline &pl : polylines){
|
|
pl.translate(bb.min);
|
|
pl.simplify(5 * spacing); // simplify to 5x line width
|
|
// Orca: round the corners of the octahedral wave. The layers where the wave degenerates to a
|
|
// straight line have no corner to round.
|
|
smooth_polyline_corners(pl, params.smooth_factor, scaled<double>(params.resolution));
|
|
}
|
|
|
|
// Apply multiline offset if needed
|
|
multiline_fill(polylines, params, spacing);
|
|
|
|
// clip pattern to boundaries, chain the clipped polylines
|
|
polylines = intersection_pl(std::move(polylines), to_polygons(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());
|
|
}
|
|
|
|
// copy from fliplines
|
|
if (!polylines.empty()) {
|
|
int infill_start_idx = polylines_out.size(); // only rotate what belongs to us.
|
|
// connect lines
|
|
chain_or_connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
|
|
|
|
// rotate back
|
|
if (std::abs(infill_angle) >= EPSILON) {
|
|
for (auto it = polylines_out.begin() + infill_start_idx; it != polylines_out.end(); ++it)
|
|
it->rotate(infill_angle);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Slic3r
|