From bd1281954d8ddfc33373d980ccb2798d15f3c2d6 Mon Sep 17 00:00:00 2001 From: Rodrigo <162915171+RF47@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:52:12 -0300 Subject: [PATCH] Better TpmsD infill implementation (#9966) Better TpmsD implementation Better TpmsD Density Adjusted --- src/libslic3r/Fill/FillTpmsD.cpp | 548 ++++++++----------------------- src/libslic3r/Fill/FillTpmsD.hpp | 31 +- 2 files changed, 154 insertions(+), 425 deletions(-) diff --git a/src/libslic3r/Fill/FillTpmsD.cpp b/src/libslic3r/Fill/FillTpmsD.cpp index a750a61920..99692b4b68 100644 --- a/src/libslic3r/Fill/FillTpmsD.cpp +++ b/src/libslic3r/Fill/FillTpmsD.cpp @@ -1,445 +1,159 @@ -#include "../ClipperUtils.hpp" -#include "FillTpmsD.hpp" #include #include #include -#include -#include -#include -#include -// From Creality Print +//#include + +#include "../ClipperUtils.hpp" +#include "../ShortestPath.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/Fill/FillBase.hpp" +#include "libslic3r/Point.hpp" +#include "libslic3r/Polygon.hpp" +#include "libslic3r/libslic3r.h" +#include "FillTpmsD.hpp" + namespace Slic3r { -using namespace std; -struct myPoint -{ - coord_t x, y; -}; -class LineSegmentMerger -{ -public: - void mergeSegments(const vector>& segments, vector>& polylines2) - { - std::unordered_map point_id_xy; - std::set> segment_ids; - std::unordered_map map_keyxy_pointid; - - auto get_itr = [&](coord_t x, coord_t y) { - for (auto i : {0}) //,-2,2 - { - for (auto j : {0}) //,-2,2 - { - int64_t combined_key1 = static_cast(x + i) << 32 | static_cast(y + j); - auto itr1 = map_keyxy_pointid.find(combined_key1); - if (itr1 != map_keyxy_pointid.end()) { - return itr1; - } - } - } - return map_keyxy_pointid.end(); - }; - - int pointid = 0; - for (const auto& segment : segments) { - coord_t x = segment.first.x; - coord_t y = segment.first.y; - auto itr = get_itr(x, y); - int segmentid0 = -1; - if (itr == map_keyxy_pointid.end()) { - int64_t combined_key = static_cast(x) << 32 | static_cast(y); - segmentid0 = pointid; - point_id_xy[pointid] = segment.first; - map_keyxy_pointid[combined_key] = pointid++; - } else { - segmentid0 = itr->second; - } - int segmentid1 = -1; - x = segment.second.x; - y = segment.second.y; - itr = get_itr(x, y); - if (itr == map_keyxy_pointid.end()) { - int64_t combined_key = static_cast(x) << 32 | static_cast(y); - segmentid1 = pointid; - point_id_xy[pointid] = segment.second; - map_keyxy_pointid[combined_key] = pointid++; - } else { - segmentid1 = itr->second; - } - - if (segmentid0 != segmentid1) { - segment_ids.insert(segmentid0 < segmentid1 ? std::make_pair(segmentid0, segmentid1) : - std::make_pair(segmentid1, segmentid0)); - } - } - - unordered_map> graph; - unordered_set visited; - vector> polylines; - - // Build the graph - for (const auto& segment : segment_ids) { - graph[segment.first].push_back(segment.second); - graph[segment.second].push_back(segment.first); - } - - vector startnodes; - for (const auto& node : graph) { - if (node.second.size() == 1) { - startnodes.push_back(node.first); - } - } - - // Find all connected components - for (const auto& point_first : startnodes) { - if (visited.find(point_first) == visited.end()) { - vector polyline; - dfs(point_first, graph, visited, polyline); - polylines.push_back(std::move(polyline)); - } - } - - for (const auto& point : graph) { - if (visited.find(point.first) == visited.end()) { - vector polyline; - dfs(point.first, graph, visited, polyline); - polylines.push_back(std::move(polyline)); - } - } - - for (auto& pl : polylines) { - vector tmpps; - for (auto& pid : pl) { - tmpps.push_back(point_id_xy[pid]); - } - polylines2.push_back(tmpps); - } - } - -private: - void dfs(const int& start_node, - std::unordered_map>& graph, - std::unordered_set& visited, - std::vector& polyline) - { - std::vector stack; - stack.reserve(graph.size()); - stack.push_back(start_node); - while (!stack.empty()) { - int node = stack.back(); - stack.pop_back(); - if (!visited.insert(node).second) { - continue; - } - polyline.push_back(node); - auto& neighbors = graph[node]; - for (const auto& neighbor : neighbors) { - if (visited.find(neighbor) == visited.end()) { - stack.push_back(neighbor); - } - } - } - } -}; - -namespace MarchingSquares { -struct Point -{ - double x, y; -}; - -vector getGridValues(int i, int j, vector>& data) -{ - vector values; - values.push_back(data[i][j + 1]); - values.push_back(data[i + 1][j + 1]); - values.push_back(data[i + 1][j]); - values.push_back(data[i][j]); - return values; -} -bool needContour(double value, double contourValue) { return value >= contourValue; } -Point interpolate(std::vector>& posxy, - std::vector p1ij, - std::vector p2ij, - double v1, - double v2, - double contourValue) -{ - Point p1; - p1.x = posxy[p1ij[0]][p1ij[1]].x; - p1.y = posxy[p1ij[0]][p1ij[1]].y; - Point p2; - p2.x = posxy[p2ij[0]][p2ij[1]].x; - p2.y = posxy[p2ij[0]][p2ij[1]].y; - - double mu = (contourValue - v1) / (v2 - v1); - Point p; - p.x = p1.x + mu * (p2.x - p1.x); - p.y = p1.y + mu * (p2.y - p1.y); - return p; +static double scaled_floor(double x,double scale){ + return std::floor(x/scale)*scale; } -void process_block(int i, - int j, - vector>& data, - double contourValue, - std::vector>& posxy, - vector& contourPoints) +static Polylines make_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height) { - vector values = getGridValues(i, j, data); - vector isNeedContour; - for (double value : values) { - isNeedContour.push_back(needContour(value, contourValue)); - } - int index = 0; - if (isNeedContour[0]) - index |= 1; - if (isNeedContour[1]) - index |= 2; - if (isNeedContour[2]) - index |= 4; - if (isNeedContour[3]) - index |= 8; - vector points; - switch (index) { - case 0: - case 15: break; + const double scaleFactor = scale_(line_spacing) / density_adjusted; - case 1: - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); + // 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, FillTpmsD::PatternTolerance) / unscale(scaleFactor); - break; - case 14: - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - break; + //scale factor for 5% : 8 712 388 + // 1z = 10^-6 mm ? + const double z = gridZ / scaleFactor; + Polylines result; - case 2: - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - - break; - case 13: - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - break; - case 3: - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - - break; - case 12: - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - - break; - case 4: - points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - - break; - case 11: - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue)); - break; - case 5: - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - points.push_back(interpolate(posxy, {i, j}, {i + 1, j}, values[3], values[2], contourValue)); - - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - break; - case 6: - points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue)); - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - - break; - case 9: - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue)); - break; - case 7: - points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue)); - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - - break; - case 8: - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j}, {i, j}, values[2], values[3], contourValue)); - break; - case 10: - points.push_back(interpolate(posxy, {i, j}, {i, j + 1}, values[3], values[0], contourValue)); - points.push_back(interpolate(posxy, {i, j}, {i + 1, j}, values[3], values[2], contourValue)); - - points.push_back(interpolate(posxy, {i, j + 1}, {i + 1, j + 1}, values[0], values[1], contourValue)); - points.push_back(interpolate(posxy, {i + 1, j + 1}, {i + 1, j}, values[1], values[2], contourValue)); - break; - } - for (Point& p : points) { - contourPoints.push_back(p); - } + //sin(x)*sin(y)*sin(z)-cos(x)*cos(y)*cos(z)=0 + //2*sin(x)*sin(y)*sin(z)-2*cos(x)*cos(y)*cos(z)=0 + //(cos(x-y)-cos(x+y))*sin(z)-(cos(x-y)+cos(x+y))*cos(z)=0 + //(sin(z)-cos(z))*cos(x-y)-(sin(z)+cos(z))*cos(x+y)=0 + double a=sin(z)-cos(z); + double b=sin(z)+cos(z); + //a*cos(x-y)-b*cos(x+y)=0 + //u=x-y, v=x+y + double minU=0-height; + double maxU=width-0; + double minV=0+0; + double maxV=width+height; + //a*cos(u)-b*cos(v)=0 + //if abs(b)std::abs(b)); + if(swapUV) { + std::swap(a,b); + std::swap(minU,minV); + std::swap(maxU,maxV); + } + std::vector> wave; + {//fill one wave + const auto v=[&](double u){return acos(a/b*cos(u));}; + for(int c=0;c<=4;++c){ + const double u=minU+2*M_PI*c/4; + wave.emplace_back(u,v(u)); + } + {//refine + int current=0; + while(current+1tolerance) + wave.emplace(wave.begin()+current+1,middleU,middleV); + else + ++current; + } + } + for(int c=1;c>& data, - std::vector>& posxy, - Polylines& repls) +// FIXME: needed to fix build on Mac on buildserver +constexpr double FillTpmsD::PatternTolerance; + +void FillTpmsD::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon expolygon, + Polylines &polylines_out) { - vector contourPoints; - int total_size = (gridSize_h - 1) * (gridSize_w - 1); - vector> contourPointss; - contourPointss.resize(total_size); - tbb::parallel_for(tbb::blocked_range(0, total_size), - [&contourValue, &posxy, &contourPointss, &data, &gridSize_w](const tbb::blocked_range& range) { - for (size_t k = range.begin(); k < range.end(); ++k) { - int i = k / (gridSize_w - 1); // - int j = k % (gridSize_w - 1); // - process_block(i, j, data, contourValue, posxy, contourPointss[k]); - } - }); - - vector> segments2; - myPoint p1, p2; - for (int k = 0; k < total_size; k++) { - for (int i = 0; i < contourPointss[k].size() / 2; i++) { - p1.x = scale_(contourPointss[k][i * 2].x); - p1.y = scale_(contourPointss[k][i * 2].y); - p2.x = scale_(contourPointss[k][i * 2 + 1].x); - p2.y = scale_(contourPointss[k][i * 2 + 1].y); - segments2.push_back({p1, p2}); - } - } - - LineSegmentMerger merger; - vector> result; - merger.mergeSegments(segments2, result); - - for (vector& p : result) { - Polyline repltmp; - for (myPoint& pt : p) { - repltmp.points.push_back(Slic3r::Point(pt.x, pt.y)); - } - repltmp.simplify(scale_(0.05f)); - repls.push_back(repltmp); - } -} -} // namespace MarchingSquares - -static float sin_table[360]; -static float cos_table[360]; -static bool g_is_init = false; - -#define PIratio 57.29577951308232 // 180/PI -static void initialize_lookup_tables() -{ - for (int i = 0; i < 360; ++i) { - float angle = i * (M_PI / 180.0); - sin_table[i] = std::sin(angle); - cos_table[i] = std::cos(angle); - } -} - -static float get_sin(float angle) -{ - angle = angle * PIratio; - int index = static_cast(std::fmod(angle, 360) + 360) % 360; - return sin_table[index]; -} - -static float get_cos(float angle) -{ - angle = angle * PIratio; - int index = static_cast(std::fmod(angle, 360) + 360) % 360; - return cos_table[index]; -} - -FillTpmsD::FillTpmsD() -{ - if (!g_is_init) { - initialize_lookup_tables(); - g_is_init = true; - } -} -void FillTpmsD::_fill_surface_single(const FillParams& params, - unsigned int thickness_layers, - const std::pair& direction, - ExPolygon expolygon, - Polylines& polylines_out) -{ - auto infill_angle = float(this->angle - (CorrectionAngle * 2 * M_PI) / 360.); - if (std::abs(infill_angle) >= EPSILON) + auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.); + if(std::abs(infill_angle) >= EPSILON) expolygon.rotate(-infill_angle); - float vari_T = 2.98 * spacing / params.density; // Infill density adjustment factor for TPMS-D + 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); - BoundingBox bb = expolygon.contour.bounding_box(); - auto cenpos = unscale(bb.center()); - auto boxsize = unscale(bb.size()); - float xlen = boxsize.x(); - float ylen = boxsize.y(); + // 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))); - float delta = 0.25f; - float myperiod = 2 * PI / vari_T; - float c_z = myperiod * this->z; - float cos_z = get_cos(c_z); - float sin_z = get_sin(c_z); + // generate pattern + Polylines polylines = make_waves( + scale_(this->z), + density_adjusted, + this->spacing, + ceil(bb.size()(0) / distance) + 1., + ceil(bb.size()(1) / distance) + 1.); - auto scalar_field = [&](float x, float y) { - // TPMS-D - float a_x = myperiod * x; - float b_y = myperiod * y; - float r = get_cos(a_x) * get_cos(b_y) * cos_z - get_sin(a_x) * get_sin(b_y) * sin_z; - return r; - }; + // shift the polyline to the grid origin + for (Polyline &pl : polylines) + pl.translate(bb.min); + - std::vector> posxy; - int i = 0, j = 0; - std::vector allptpos; - for (float y = -(ylen) / 2.0f - 2; y < (ylen) / 2.0f + 2; y = y + delta, i++) { - j = 0; - std::vector colposxy; - for (float x = -(xlen) / 2.0f - 2; x < (xlen) / 2.0f + 2; x = x + delta, j++) { - MarchingSquares::Point pt; - pt.x = cenpos.x() + x; - pt.y = cenpos.y() + y; - colposxy.push_back(pt); - } - posxy.push_back(colposxy); + polylines = intersection_pl(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()); } - std::vector> data(posxy.size(), std::vector(posxy[0].size())); - - int width = posxy[0].size(); - int height = posxy.size(); - int total_size = (height) * (width); - tbb::parallel_for(tbb::blocked_range(0, total_size), - [&width, &scalar_field, &data, &posxy](const tbb::blocked_range& range) { - for (size_t k = range.begin(); k < range.end(); ++k) { - int i = k / (width); - int j = k % (width); - data[i][j] = scalar_field(posxy[i][j].x, posxy[i][j].y); - } - }); - - Polylines polylines; - MarchingSquares::drawContour(0, j, i, data, posxy, polylines); - - polylines = intersection_pl(polylines, expolygon); - - if (!polylines.empty()) { - // connect lines - size_t polylines_out_first_idx = polylines_out.size(); - if (params.dont_connect()) - append(polylines_out, chain_polylines(polylines)); + 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 + + // 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); - } + for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it) + it->rotate(infill_angle); + } } } diff --git a/src/libslic3r/Fill/FillTpmsD.hpp b/src/libslic3r/Fill/FillTpmsD.hpp index cf5d5f69fa..73b55e1e1c 100644 --- a/src/libslic3r/Fill/FillTpmsD.hpp +++ b/src/libslic3r/Fill/FillTpmsD.hpp @@ -1,31 +1,46 @@ #ifndef slic3r_FillTpmsD_hpp_ #define slic3r_FillTpmsD_hpp_ -#include "../libslic3r.h" +#include + +#include "libslic3r/libslic3r.h" #include "FillBase.hpp" +#include "libslic3r/ExPolygon.hpp" +#include "libslic3r/Polyline.hpp" namespace Slic3r { +class Point; class FillTpmsD : public Fill { public: - FillTpmsD(); + FillTpmsD() {} Fill* clone() const override { return new FillTpmsD(*this); } // require bridge flow since most of this pattern hangs in air bool use_bridge_flow() const override { return false; } + // Correction applied to regular infill angle to maximize printing // speed in default configuration (degrees) static constexpr float CorrectionAngle = -45.; - void _fill_surface_single(const FillParams& params, - unsigned int thickness_layers, - const std::pair& direction, - ExPolygon expolygon, - Polylines& polylines_out) override; + // Density adjustment to have a good %of weight. + static constexpr double DensityAdjust = 2.1; + + // Gyroid upper resolution tolerance (mm^-2) + static constexpr double PatternTolerance = 0.1; + + +protected: + void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon expolygon, + Polylines &polylines_out) override; }; } // namespace Slic3r -#endif // slic3r_FillTpmsD_hpp_ +#endif // slic3r_FillTpmsD_hpp_ \ No newline at end of file