diff --git a/resources/images/param_tpmsfk.svg b/resources/images/param_tpmsfk.svg
new file mode 100644
index 0000000000..5bc8d2d793
--- /dev/null
+++ b/resources/images/param_tpmsfk.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 2e56523334..925728e5a1 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -104,6 +104,8 @@ set(lisbslic3r_sources
Fill/FillGyroid.hpp
Fill/FillTpmsD.cpp
Fill/FillTpmsD.hpp
+ Fill/FillTpmsFK.cpp
+ Fill/FillTpmsFK.hpp
Fill/FillPlanePath.cpp
Fill/FillPlanePath.hpp
Fill/FillLine.cpp
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index 2e83d0829f..87370b8075 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -15,6 +15,7 @@
#include "FillLightning.hpp"
#include "FillConcentricInternal.hpp"
#include "FillTpmsD.hpp"
+#include "FillTpmsFK.hpp"
#include "FillConcentric.hpp"
#include "libslic3r.h"
@@ -1330,6 +1331,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
case ip3DHoneycomb:
case ipGyroid:
case ipTpmsD:
+ case ipTpmsFK:
case ipHilbertCurve:
case ipArchimedeanChords:
case ipOctagramSpiral:
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 8936f5f55f..7f6d2e7229 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -18,6 +18,7 @@
#include "Fill3DHoneycomb.hpp"
#include "FillGyroid.hpp"
#include "FillTpmsD.hpp"
+#include "FillTpmsFK.hpp"
#include "FillPlanePath.hpp"
#include "FillLine.hpp"
#include "FillRectilinear.hpp"
@@ -44,6 +45,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ip3DHoneycomb: return new Fill3DHoneycomb();
case ipGyroid: return new FillGyroid();
case ipTpmsD: return new FillTpmsD();//from creality print
+ case ipTpmsFK: return new FillTpmsFK();
case ipRectilinear: return new FillRectilinear();
case ipAlignedRectilinear: return new FillAlignedRectilinear();
case ipCrossHatch: return new FillCrossHatch();
diff --git a/src/libslic3r/Fill/FillTpmsFK.cpp b/src/libslic3r/Fill/FillTpmsFK.cpp
new file mode 100644
index 0000000000..aa4a9442f6
--- /dev/null
+++ b/src/libslic3r/Fill/FillTpmsFK.cpp
@@ -0,0 +1,543 @@
+#include "../ClipperUtils.hpp"
+#include "FillTpmsFK.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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 denom = v2 - v1;
+ double mu;
+ if (std::abs(denom) < 1e-12) {
+ // avoid division by zero
+ mu = 0.5;
+ } else {
+ mu = (contourValue - v1) / denom;
+ }
+ Point p;
+ p.x = p1.x + mu * (p2.x - p1.x);
+ p.y = p1.y + mu * (p2.y - p1.y);
+ return p;
+}
+
+void process_block(int i,
+ int j,
+ vector>& data,
+ double contourValue,
+ std::vector>& posxy,
+ vector& contourPoints)
+{
+ 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;
+
+ 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));
+
+ 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;
+
+ 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);
+ }
+}
+
+// --- Chaikin Smooth ---
+
+static Polyline chaikin_smooth(Polyline poly, int iterations , double weight )
+{
+ if (poly.points.size() < 3) return poly;
+
+ const double w1 = 1.0 - weight;
+ decltype(poly.points) buffer;
+ buffer.reserve(poly.points.size() * 2);
+
+ for (int it = 0; it < iterations; ++it) {
+ buffer.clear();
+ buffer.push_back(poly.points.front());
+
+ for (size_t i = 0; i < poly.points.size() - 1; ++i) {
+ const auto &p0 = poly.points[i];
+ const auto &p1 = poly.points[i + 1];
+
+ buffer.emplace_back(
+ p0.x() * w1 + p1.x() * weight,
+ p0.y() * w1 + p1.y() * weight
+ );
+ buffer.emplace_back(
+ p0.x() * weight + p1.x() * w1,
+ p0.y() * weight + p1.y() * w1
+ );
+ }
+
+ buffer.push_back(poly.points.back());
+ poly.points.swap(buffer);
+ }
+
+ return poly;
+}
+
+
+void drawContour(double contourValue,
+ int gridSize_w,
+ int gridSize_h,
+ vector>& data,
+ std::vector>& posxy,
+ Polylines& repls,
+ const FillParams& params)
+{
+
+ if (data.empty() || data[0].empty()) {
+
+ return;
+ }
+ gridSize_h = static_cast(data.size());
+ gridSize_w = static_cast(data[0].size());
+
+
+ if (static_cast(posxy.size()) != gridSize_h || static_cast(posxy[0].size()) != gridSize_w) {
+
+ return;
+ }
+
+ int total_size = (gridSize_h - 1) * (gridSize_w - 1);
+ vector> contourPointss(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 = static_cast(k) / (gridSize_w - 1);
+ int j = static_cast(k) % (gridSize_w - 1);
+
+ if (i + 1 < static_cast(data.size()) && j + 1 < static_cast(data[0].size())) {
+ 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 < static_cast(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));
+ }
+ // symplify tolerance based on density
+ const float min_tolerance = 0.005f;
+ const float max_tolerance = 0.2f;
+ float simplify_tolerance = (0.005f / params.density);
+ simplify_tolerance = std::clamp(simplify_tolerance, min_tolerance, max_tolerance);
+ repltmp.simplify(scale_(simplify_tolerance));
+ repltmp = chaikin_smooth(repltmp, 2, 0.25);
+ repls.push_back(repltmp);
+ }
+}
+
+} // namespace MarchingSquares
+
+static float sin_table[360];
+static float cos_table[360];
+static std::once_flag trig_tables_once_flag;
+
+#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);
+ }
+}
+
+
+inline static void ensure_trig_tables_initialized()
+{
+ std::call_once(trig_tables_once_flag, initialize_lookup_tables);
+}
+
+inline static float get_sin(float angle)
+{
+ angle = angle * PIratio;
+ int index = static_cast(std::fmod(angle, 360) + 360) % 360;
+ return sin_table[index];
+}
+
+inline static float get_cos(float angle)
+{
+ angle = angle * PIratio;
+ int index = static_cast(std::fmod(angle, 360) + 360) % 360;
+ return cos_table[index];
+}
+
+void FillTpmsFK::_fill_surface_single(const FillParams& params,
+ unsigned int thickness_layers,
+ const std::pair& direction,
+ ExPolygon expolygon,
+ Polylines& polylines_out)
+{
+ ensure_trig_tables_initialized();
+
+ auto infill_angle = float(this->angle + (CorrectionAngle * 2 * M_PI) / 360.);
+ if(std::abs(infill_angle) >= EPSILON)
+ expolygon.rotate(-infill_angle);
+
+ float density_factor = std::min(0.9f, params.density);
+ // Density adjusted to have a good %of weight.
+ const float vari_T = 4.18f * spacing * params.multiline / density_factor;
+
+ BoundingBox bb = expolygon.contour.bounding_box();
+ auto cenpos = unscale(bb.center());
+ auto boxsize = unscale(bb.size());
+ float xlen = boxsize.x();
+ float ylen = boxsize.y();
+
+ const float delta = 0.5f; // mesh step (adjust for quality/performance)
+
+ float myperiod = 2 * PI / vari_T;
+ float c_z = myperiod * this->z; // z height
+
+ // scalar field Fischer-Koch
+ auto scalar_field = [&](float x, float y) {
+ float a_x = myperiod * x;
+ float b_y = myperiod * y;
+
+ // Fischer - Koch S equation:
+ // cos(2x)sin(y)cos(z) + cos(2y)sin(z)cos(x) + cos(2z)sin(x)cos(y) = 0
+ const float cos2ax = get_cos(2*a_x);
+ const float cos2by = get_cos(2*b_y);
+ const float cos2cz = get_cos(2*c_z);
+ const float sinby = get_sin(b_y);
+ const float cosax = get_cos(a_x);
+ const float sinax = get_sin(a_x);
+ const float cosby = get_cos(b_y);
+ const float sincz = get_sin(c_z);
+ const float coscz = get_cos(c_z);
+
+ return cos2ax * sinby * coscz
+ + cos2by * sincz * cosax
+ + cos2cz * sinax * cosby;
+ };
+
+ // Mesh generation
+ std::vector> posxy;
+ int i = 0, j = 0;
+ 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);
+ }
+
+ 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;
+ const double contour_value = 0.075; // offset from zero to avoid numerical issues
+ MarchingSquares::drawContour(contour_value, width , height , data, posxy, polylines, params);
+
+ if (!polylines.empty()) {
+ // Apply multiline offset if needed
+ multiline_fill(polylines, params, spacing);
+
+
+ polylines = intersection_pl(polylines, expolygon);
+
+ // 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());
+
+ // 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
diff --git a/src/libslic3r/Fill/FillTpmsFK.hpp b/src/libslic3r/Fill/FillTpmsFK.hpp
new file mode 100644
index 0000000000..b9cd7d208b
--- /dev/null
+++ b/src/libslic3r/Fill/FillTpmsFK.hpp
@@ -0,0 +1,40 @@
+#ifndef slic3r_FillTpmsFK_hpp_
+#define slic3r_FillTpmsFK_hpp_
+
+#include
+
+#include "libslic3r/libslic3r.h"
+#include "FillBase.hpp"
+#include "libslic3r/ExPolygon.hpp"
+#include "libslic3r/Polyline.hpp"
+
+namespace Slic3r {
+class Point;
+
+class FillTpmsFK : public Fill
+{
+public:
+ FillTpmsFK() {}
+ Fill* clone() const override { return new FillTpmsFK(*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;
+
+ bool is_self_crossing() override { return false; }
+
+};
+
+} // namespace Slic3r
+
+#endif // slic3r_FillTpmsFK_hpp_
\ No newline at end of file
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index 326d1fffbe..60cc113906 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -392,6 +392,7 @@ coordf_t Layer::get_sparse_infill_max_void_area()
case ipLine:
case ipGyroid:
case ipTpmsD:
+ case ipTpmsFK:
case ipAlignedRectilinear:
case ipOctagramSpiral:
case ipHilbertCurve:
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 3ae3509b24..3db9b6cfbd 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -165,6 +165,7 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "2dlattice", ip2DLattice },
{ "crosshatch", ipCrossHatch },
{ "tpmsd", ipTpmsD },
+ { "tpmsfk", ipTpmsFK },
{ "gyroid", ipGyroid },
{ "concentric", ipConcentric },
{ "hilbertcurve", ipHilbertCurve },
@@ -2417,6 +2418,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("2dlattice");
def->enum_values.push_back("crosshatch");
def->enum_values.push_back("tpmsd");
+ def->enum_values.push_back("tpmsfk");
def->enum_values.push_back("gyroid");
def->enum_values.push_back("concentric");
def->enum_values.push_back("hilbertcurve");
@@ -2442,6 +2444,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("2D Lattice"));
def->enum_labels.push_back(L("Cross Hatch"));
def->enum_labels.push_back(L("TPMS-D"));
+ def->enum_labels.push_back(L("TPMS-FK"));
def->enum_labels.push_back(L("Gyroid"));
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Hilbert Curve"));
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index bcd71cefea..c2ec764ec3 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -71,7 +71,7 @@ enum InfillPattern : int {
ipTriangles, ipStars,
ipCubic, ipAdaptiveCubic, ipQuarterCubic, ipSupportCubic, ipLightning,
ipHoneycomb, ip3DHoneycomb, ip2DHoneycomb, ip2DLattice,
- ipCrossHatch, ipTpmsD, ipGyroid,
+ ipCrossHatch, ipTpmsD, ipTpmsFK, ipGyroid,
ipConcentric, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
ipSupportBase, ipConcentricInternal,
ipCount,
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 01417fcbca..78d5027744 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -566,7 +566,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
// Infill patterns that support multiline infill.
InfillPattern pattern = config->opt_enum("sparse_infill_pattern");
- bool have_multiline_infill_pattern = pattern == ipGyroid || pattern == ipGrid || pattern == ipRectilinear || pattern == ipTpmsD || pattern == ipCrossHatch || pattern == ipHoneycomb || pattern == ip2DLattice || pattern == ip2DHoneycomb ||
+ bool have_multiline_infill_pattern = pattern == ipGyroid || pattern == ipGrid || pattern == ipRectilinear || pattern == ipTpmsD || pattern == ipTpmsFK || pattern == ipCrossHatch || pattern == ipHoneycomb || pattern == ip2DLattice || pattern == ip2DHoneycomb ||
pattern == ipCubic || pattern == ipStars || pattern == ipAlignedRectilinear || pattern == ipLightning || pattern == ip3DHoneycomb || pattern == ipAdaptiveCubic || pattern == ipSupportCubic;
toggle_line("fill_multiline", have_multiline_infill_pattern);