Skip to content

Commit

Permalink
Add ability for infill to support lines within skins, and add config …
Browse files Browse the repository at this point in the history
…option and tests for the same
  • Loading branch information
Hello1024 committed Jul 29, 2024
1 parent a91377c commit 8d0e2ae
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 38 deletions.
13 changes: 13 additions & 0 deletions include/settings/EnumSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ enum class EFillMethod
PLUGIN, // Place plugin after none to prevent it from being tested in the gtest suite.
};


/*!
* Enum for the value of extra_infill_lines_to_support_skins
* This enum defines what extra lines should be added to infill to support
* skins above.
*/
enum class EExtraInfillLinesToSupportSkins
{
WALLS_AND_LINES,
WALLS,
NONE,
};

/*!
* Type of platform adhesion.
*/
Expand Down
37 changes: 33 additions & 4 deletions src/FffGcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,10 @@ void addExtraLinesToSupportSurfacesAbove(
{
// Where needs support?

const auto enabled = mesh.settings.get<EExtraInfillLinesToSupportSkins>("extra_infill_lines_to_support_skins");
if (enabled == EExtraInfillLinesToSupportSkins::NONE)
return;

const size_t skin_layer_nr = gcode_layer.getLayerNr() + 1 + mesh.settings.get<size_t>("skin_edge_support_layers");
if (skin_layer_nr >= mesh.layers.size())
return;
Expand Down Expand Up @@ -2236,9 +2240,31 @@ void addExtraLinesToSupportSurfacesAbove(
infill_comp.generate(skin_paths, skin_polygons, skin_lines, mesh.settings, 0, SectionType::SKIN);

wall_tool_paths2lines({ skin_paths }, printed_lines_on_layer_above);
for (const Polygon& poly : skin_polygons)
printed_lines_on_layer_above.push_back(poly.toPseudoOpenPolyline());
printed_lines_on_layer_above.push_back(skin_lines);
if (enabled == EExtraInfillLinesToSupportSkins::WALLS_AND_LINES)
{
for (const Polygon& poly : skin_polygons)
printed_lines_on_layer_above.push_back(poly.toPseudoOpenPolyline());
printed_lines_on_layer_above.push_back(skin_lines);
}
}
}

/* move all points "inwards" by line_width to ensure a good overlap.
* Eg. Old Point New Point
* | |
* | X|
* -------X ---------
*/
for (OpenPolyline& poly : printed_lines_on_layer_above)
{
OpenPolyline copy = poly;
auto orig_it = poly.begin();
for (auto it = copy.begin(); it != copy.end(); ++it, ++orig_it)
{
if (it > copy.begin())
*orig_it += normal(*(it) - *(it - 1), infill_line_width / 2);
if (it < copy.end() - 1)
*orig_it += normal(*(it + 1) - *(it), infill_line_width / 2);
}
}

Expand All @@ -2248,12 +2274,15 @@ void addExtraLinesToSupportSurfacesAbove(
// What shape is the supporting infill?
OpenLinesSet support_lines;
support_lines.push_back(infill_lines);
// The edge of the infill area is also considered supported
for (const auto& poly : part.getOwnInfillArea())
{
support_lines.push_back(poly.toPseudoOpenPolyline());
}
// Infill walls can support the layer above
wall_tool_paths2lines(wall_tool_paths, support_lines);

// Turn the lines into a giant shape.
Shape supported_area = support_lines.offset(infill_line_width / 2);
if (supported_area.empty())
return;
Expand Down Expand Up @@ -2293,7 +2322,7 @@ void addExtraLinesToSupportSurfacesAbove(
const PointsSet& points = pair.second;

OpenLinesSet result_lines;
getBestAngledLinesToSupportPoints(result_lines, Shape(area).offset(infill_line_width / 2), points, infill_line_width);
getBestAngledLinesToSupportPoints(result_lines, Shape(area).offset(infill_line_width / 2 + 10), points, infill_line_width);

for (const auto& line : part.getOwnInfillArea().intersection(result_lines))
{
Expand Down
18 changes: 18 additions & 0 deletions src/settings/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,24 @@ EPlatformAdhesion Settings::get<EPlatformAdhesion>(const std::string& key) const
}
}

template<>
EExtraInfillLinesToSupportSkins Settings::get<EExtraInfillLinesToSupportSkins>(const std::string& key) const
{
const std::string& value = get<std::string>(key);
using namespace cura::utils;
switch (hash_enum(value))
{
case "walls_and_lines"_sw:
return EExtraInfillLinesToSupportSkins::WALLS_AND_LINES;
case "walls"_sw:
return EExtraInfillLinesToSupportSkins::WALLS;
case "none"_sw:
return EExtraInfillLinesToSupportSkins::NONE;
default:
return EExtraInfillLinesToSupportSkins::WALLS_AND_LINES;
}
}

template<>
ESupportType Settings::get<ESupportType>(const std::string& key) const
{
Expand Down
96 changes: 62 additions & 34 deletions tests/FffGcodeWriterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@

#include "FffGcodeWriter.h" //Unit under test.

#include <unordered_set>
#include <iostream>
#include <fstream>
#include <filesystem>

#include <fstream>
#include <iostream>
#include <unordered_set>

#include <range/v3/view/join.hpp>
#include <scripta/logger.h>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "Application.h"
#include "LayerPlan.h"
#include "Slice.h"
#include "arcus/MockCommunication.h" // To prevent calls to any missing Communication class.
#include "geometry/OpenPolyline.h"
#include "geometry/Polygon.h" //To create example polygons.
#include "settings/Settings.h" //Settings to generate walls with.
#include "sliceDataStorage.h" //Sl
#include "LayerPlan.h"
#include "Slice.h"
#include "Application.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>

// NOLINTBEGIN(*-magic-numbers)
namespace cura
Expand All @@ -34,37 +35,34 @@ class FffGcodeWriterTest : public testing::Test
Settings* settings;
FffGcodeWriter fff_gcode_writer;

Shape square_shape;
Shape outer_square;
// Square that fits wholly inside the above square
Shape inner_square;

FffGcodeWriterTest()
: fff_gcode_writer()
{
square_shape.emplace_back();
square_shape.back().emplace_back(0, 0);
square_shape.back().emplace_back(MM2INT(100), 0);
square_shape.back().emplace_back(MM2INT(100), MM2INT(100));
square_shape.back().emplace_back(0, MM2INT(100));
outer_square.emplace_back();
outer_square.back().emplace_back(0, 0);
outer_square.back().emplace_back(MM2INT(100), 0);
outer_square.back().emplace_back(MM2INT(100), MM2INT(100));
outer_square.back().emplace_back(0, MM2INT(100));

inner_square.emplace_back();
inner_square.back().emplace_back(MM2INT(10), MM2INT(20));
inner_square.back().emplace_back(MM2INT(60), MM2INT(20));
inner_square.back().emplace_back(MM2INT(60), MM2INT(60));
inner_square.back().emplace_back(MM2INT(10), MM2INT(60));

Application::getInstance().communication_ = new MockCommunication();
}

SliceDataStorage* setUpStorage()
{
constexpr size_t num_mesh_groups = 1;
Application::getInstance().current_slice_ = new Slice(num_mesh_groups);
Application::getInstance().current_slice_ = new Slice(1);

// Define all settings in the mesh group. The extruder train and model settings will fall back on that then.
settings = &Application::getInstance().current_slice_->scene.current_mesh_group->settings;
// Default settings. These are not (always) the FDM printer defaults, but sometimes just setting values that can be recognised
// uniquely as much as possible.

settings = &Application::getInstance().current_slice_->scene.settings;

const auto path = std::filesystem::path(__FILE__).parent_path().append("test_default_settings.txt").string();
std::ifstream file(path);
Expand All @@ -75,7 +73,9 @@ class FffGcodeWriterTest : public testing::Test
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
settings->add(key, value);
}
}

settings->add("infill_line_distance", "10");

Application::getInstance().current_slice_->scene.extruders.emplace_back(0, settings); // Add an extruder train.

Expand All @@ -99,7 +99,6 @@ class FffGcodeWriterTest : public testing::Test
TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt)
{
// SETUP

SliceDataStorage* storage = setUpStorage();

// Set the fan speed layer time settings (since the LayerPlan constructor copies these).
Expand All @@ -120,8 +119,8 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt)
MeshPathConfigs mesh_config(mesh_storage, 10, 100, {0.5});
SliceLayerPart part;

part.infill_area_per_combine_per_density = {{square_shape}};
part.infill_area = square_shape;
part.infill_area_per_combine_per_density = { { outer_square } };
part.infill_area = outer_square;

mesh_storage.layers[101].parts.emplace_back();
SliceLayerPart& top_part = mesh_storage.layers[101].parts.back();
Expand All @@ -143,6 +142,14 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt)
part
);

/* Useful code if you're debugging this test. Also add this test as a friend in GCodeExport.h
GCodeExport gcode_export;
std::ofstream output_file;
output_file.open("test_result.gcode");
gcode_export.output_stream_ = &output_file;
gcode_layer.writeGCode(gcode_export);
*/

// Test helper
auto checkPointIsPassed = [&](Point2LL p, coord_t margin)-> bool {
Point2LL last;
Expand All @@ -162,16 +169,37 @@ TEST_F(FffGcodeWriterTest, SurfaceGetsExtraInfillLinesUnderIt)
// Check the results
for (auto poly:inner_square)
for (auto point:poly)
EXPECT_TRUE(checkPointIsPassed(point, MM2INT(1))) << "The corners of this square need an infill line under them so they dont droop down!";
EXPECT_TRUE(checkPointIsPassed(point, MM2INT(0.3))) << "The corners of this square need an infill line under them so they dont droop down!";

int ctr = 0;
if (checkPointIsPassed({MM2INT(30),MM2INT(30)}, MM2INT(1))) ctr++;
if (checkPointIsPassed({MM2INT(8),MM2INT(64)}, MM2INT(1))) ctr++;
if (checkPointIsPassed({MM2INT(16),MM2INT(42)}, MM2INT(1))) ctr++;
if (checkPointIsPassed({MM2INT(77),MM2INT(33)}, MM2INT(1))) ctr++;
if (checkPointIsPassed({MM2INT(12),MM2INT(1)}, MM2INT(1))) ctr++;
if (checkPointIsPassed({MM2INT(30),MM2INT(30)}, MM2INT(1))) ctr++;
EXPECT_LE(ctr, 3) << "Randomly selected points should not be supported by sparse infill";
if (checkPointIsPassed({ MM2INT(90), MM2INT(30) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(8), MM2INT(64) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(5), MM2INT(72) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(77), MM2INT(33) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(12), MM2INT(1) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(88), MM2INT(70) }, MM2INT(0.3)))
ctr++;
EXPECT_LE(ctr, 3) << "Selected points outside the square should not be supported by sparse infill";

ctr = 0;
if (checkPointIsPassed({ MM2INT(30), MM2INT(32) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(40), MM2INT(35) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(35), MM2INT(49) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(21), MM2INT(42) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(48), MM2INT(32) }, MM2INT(0.3)))
ctr++;
if (checkPointIsPassed({ MM2INT(29), MM2INT(45) }, MM2INT(0.3)))
ctr++;
EXPECT_LE(ctr, 3) << "Selected points in the middle of the square should not be supported by sparse infill";
}

} // namespace cura
Expand Down
1 change: 1 addition & 0 deletions tests/test_default_settings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ machine_nozzle_heat_up_speed=2.0
jerk_wall_0=20
retraction_extra_prime_amount=0
skin_edge_support_thickness=0
extra_infill_lines_to_support_skins=walls_and_lines
speed_topbottom=40.0
jerk_prime_tower=20
retraction_hop=1
Expand Down

0 comments on commit 8d0e2ae

Please sign in to comment.