diff --git a/resources/images/param_tpmsd.svg b/resources/images/param_tpmsd.svg
new file mode 100644
index 0000000000..247f77c6d2
--- /dev/null
+++ b/resources/images/param_tpmsd.svg
@@ -0,0 +1,192 @@
+
+
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index b93f025e73..e1533b7f05 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -101,6 +101,8 @@ set(lisbslic3r_sources
Fill/FillHoneycomb.hpp
Fill/FillGyroid.cpp
Fill/FillGyroid.hpp
+ Fill/FillTpmsD.cpp
+ Fill/FillTpmsD.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 283ff2caa5..ec4afe9e9d 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -14,6 +14,7 @@
#include "FillRectilinear.hpp"
#include "FillLightning.hpp"
#include "FillConcentricInternal.hpp"
+#include "FillTpmsD.hpp"
#include "FillConcentric.hpp"
#include "libslic3r.h"
@@ -1047,6 +1048,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
case ipHoneycomb:
case ip3DHoneycomb:
case ipGyroid:
+ case ipTpmsD:
case ipHilbertCurve:
case ipArchimedeanChords:
case ipOctagramSpiral: break;
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 1430e678bc..619f67036c 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -16,6 +16,7 @@
#include "FillHoneycomb.hpp"
#include "Fill3DHoneycomb.hpp"
#include "FillGyroid.hpp"
+#include "FillTpmsD.hpp"
#include "FillPlanePath.hpp"
#include "FillLine.hpp"
#include "FillRectilinear.hpp"
@@ -41,6 +42,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipHoneycomb: return new FillHoneycomb();
case ip3DHoneycomb: return new Fill3DHoneycomb();
case ipGyroid: return new FillGyroid();
+ case ipTpmsD: return new FillTpmsD();//from creality print
case ipRectilinear: return new FillRectilinear();
case ipAlignedRectilinear: return new FillAlignedRectilinear();
case ipCrossHatch: return new FillCrossHatch();
diff --git a/src/libslic3r/Fill/FillTpmsD.cpp b/src/libslic3r/Fill/FillTpmsD.cpp
new file mode 100644
index 0000000000..a750a61920
--- /dev/null
+++ b/src/libslic3r/Fill/FillTpmsD.cpp
@@ -0,0 +1,446 @@
+#include "../ClipperUtils.hpp"
+#include "FillTpmsD.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+// From Creality Print
+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;
+}
+
+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);
+ }
+}
+
+void drawContour(double contourValue,
+ int gridSize_w,
+ int gridSize_h,
+ vector>& data,
+ std::vector>& posxy,
+ Polylines& repls)
+{
+ 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)
+ 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();
+ auto cenpos = unscale(bb.center());
+ auto boxsize = unscale(bb.size());
+ float xlen = boxsize.x();
+ float ylen = boxsize.y();
+
+ 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);
+
+ 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;
+ };
+
+ 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);
+ }
+
+ 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));
+ 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
\ No newline at end of file
diff --git a/src/libslic3r/Fill/FillTpmsD.hpp b/src/libslic3r/Fill/FillTpmsD.hpp
new file mode 100644
index 0000000000..cf5d5f69fa
--- /dev/null
+++ b/src/libslic3r/Fill/FillTpmsD.hpp
@@ -0,0 +1,31 @@
+#ifndef slic3r_FillTpmsD_hpp_
+#define slic3r_FillTpmsD_hpp_
+
+#include "../libslic3r.h"
+#include "FillBase.hpp"
+
+namespace Slic3r {
+
+class FillTpmsD : public Fill
+{
+public:
+ 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;
+};
+
+} // namespace Slic3r
+
+#endif // slic3r_FillTpmsD_hpp_
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index 4d717381fe..f6047fbbb1 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -383,6 +383,7 @@ coordf_t Layer::get_sparse_infill_max_void_area()
case ipRectilinear:
case ipLine:
case ipGyroid:
+ case ipTpmsD:
case ipAlignedRectilinear:
case ipOctagramSpiral:
case ipHilbertCurve:
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index c77732fdb9..b02f0ad021 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -146,6 +146,7 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "triangles", ipTriangles },
{ "tri-hexagon", ipStars },
{ "gyroid", ipGyroid },
+ { "tpmsd", ipTpmsD },//TpmsD from CrealityPrint
{ "honeycomb", ipHoneycomb },
{ "adaptivecubic", ipAdaptiveCubic },
{ "monotonic", ipMonotonic },
@@ -158,7 +159,7 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "supportcubic", ipSupportCubic },
{ "lightning", ipLightning },
{ "crosshatch", ipCrossHatch},
- { "quartercubic", ipQuarterCubic}
+ { "quartercubic", ipQuarterCubic},
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
@@ -2385,6 +2386,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("triangles");
def->enum_values.push_back("tri-hexagon");
def->enum_values.push_back("gyroid");
+ def->enum_values.push_back("tpmsd");
def->enum_values.push_back("honeycomb");
def->enum_values.push_back("adaptivecubic");
def->enum_values.push_back("alignedrectilinear");
@@ -2405,6 +2407,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Triangles"));
def->enum_labels.push_back(L("Tri-hexagon"));
def->enum_labels.push_back(L("Gyroid"));
+ def->enum_labels.push_back(L("TPMS-D"));
def->enum_labels.push_back(L("Honeycomb"));
def->enum_labels.push_back(L("Adaptive Cubic"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index d244e7bab2..09f81e7f2b 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -58,7 +58,7 @@ enum AuthorizationType {
};
enum InfillPattern : int {
- ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
+ ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipTpmsD, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
ipLightning, ipCrossHatch, ipQuarterCubic,
ipCount,