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,