Merge branch 'main' into feat/generic-n-extruders

This commit is contained in:
peachismomo
2026-10-04 01:58:50 +08:00
1761 changed files with 47188 additions and 9885 deletions
+6
View File
@@ -1,10 +1,16 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "test_helpers.hpp"
#include <algorithm>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/libslic3r.h"
#include <sstream>
#include <string>
#include <utility>
#include <vector>
using namespace Slic3r;
@@ -1,7 +1,12 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <cstdlib>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Point.hpp"
@@ -1,6 +1,13 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/benchmark/catch_benchmark.hpp>
#include "libslic3r/AABBTreeLines.hpp"
#include "libslic3r/GCode.hpp"
#include "libslic3r/GCode/ExtrusionProcessor.hpp"
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/TriangleMesh.hpp"
@@ -9,7 +16,17 @@
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <functional>
#include "libslic3r/Line.hpp"
#include "libslic3r/Point.hpp"
#include <cstddef>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Model.hpp"
#include <string>
#include <string_view>
#include <vector>
using namespace Slic3r;
@@ -524,6 +541,187 @@ TEST_CASE("A wall is left whole where neither its speed nor its cooling changes"
REQUIRE(points.size() == 3);
}
namespace {
// The caged overhang box, sliced, and a layer on its slope.
struct SlicedCage
{
Print print;
Model model;
const PrintObject *object{nullptr};
const Layer *layer{nullptr};
explicit SlicedCage(const DynamicPrintConfig &config = caged_overhang_config("classic"))
{
init_print(std::vector<TriangleMesh>{caged_overhang_mesh()}, print, model, config, nullptr, false);
print.process();
object = print.objects().front();
layer = object->get_layer(int(std::lround((caged_slope_z_min + caged_slope_z_max) / 2. / caged_layer_height)));
}
};
using Walls = std::vector<std::vector<ProcessedPoint>>;
// Estimates every wall of `layer` against whatever layer `estimator` was last prepared with before it.
Walls estimate_walls(ExtrusionQualityEstimator &estimator, const PrintObject *object, const Layer &layer)
{
const ConfigOptionPercents overlaps({90, 75, 50, 25, 13, 0});
const ConfigOptionFloatsOrPercents speeds({FloatOrPercent{100, true}, FloatOrPercent{50, true}, FloatOrPercent{30, true},
FloatOrPercent{20, true}, FloatOrPercent{10, true}, FloatOrPercent{5, true}});
Walls walls;
estimator.set_current_object(object);
for (const LayerRegion *region : layer.regions())
for_each_extrusion_path(region->perimeters, [&](const ExtrusionPath &path) {
if (is_perimeter(path.role()))
walls.push_back(estimator.estimate_extrusion_quality(path, overlaps, speeds, caged_outer_wall_speed, caged_outer_wall_speed,
true, 0.5f));
});
return walls;
}
uint32_t float_bits(float value)
{
uint32_t bits;
std::memcpy(&bits, &value, sizeof(bits));
return bits;
}
bool same_point(const ProcessedPoint &a, const ProcessedPoint &b)
{
return a.p == b.p && float_bits(a.speed) == float_bits(b.speed) && float_bits(a.overlap) == float_bits(b.overlap);
}
// Requires the walls to match point for point, bit for bit.
void check_identical(const Walls &actual, const Walls &expected)
{
REQUIRE(actual.size() == expected.size());
for (size_t wall = 0; wall < actual.size(); ++wall) {
INFO("wall " << wall);
REQUIRE(actual[wall].size() == expected[wall].size());
for (size_t i = 0; i < actual[wall].size(); ++i) {
const ProcessedPoint &a = actual[wall][i];
const ProcessedPoint &e = expected[wall][i];
INFO("point " << i << ": speed " << a.speed << " vs " << e.speed << ", overlap " << a.overlap << " vs " << e.overlap);
CHECK(a.p == e.p);
CHECK(float_bits(a.speed) == float_bits(e.speed));
CHECK(float_bits(a.overlap) == float_bits(e.overlap));
}
}
}
bool any_difference(const Walls &a, const Walls &b)
{
return !std::equal(a.begin(), a.end(), b.begin(), b.end(), [](const std::vector<ProcessedPoint> &wa, const std::vector<ProcessedPoint> &wb) {
return std::equal(wa.begin(), wa.end(), wb.begin(), wb.end(), same_point);
});
}
bool any_slowed(const Walls &walls)
{
return std::any_of(walls.begin(), walls.end(), [](const std::vector<ProcessedPoint> &wall) {
return std::any_of(wall.begin(), wall.end(), [](const ProcessedPoint &point) { return point.speed < caged_outer_wall_speed; });
});
}
} // namespace
TEST_CASE("Overhang data computed ahead of the generator gives the same wall speeds", "[ExtrusionProcessor]")
{
const SlicedCage cage;
REQUIRE(cage.layer->lower_layer != nullptr);
ExtrusionQualityEstimator queried;
queried.prepare_for_new_layer(cage.object, cage.layer->lower_layer);
queried.prepare_for_new_layer(cage.object, cage.layer);
const Walls expected = estimate_walls(queried, cage.object, *cage.layer);
REQUIRE(any_slowed(expected));
ExtrusionQualityEstimator precomputed;
precomputed.prepare_for_new_layer(cage.object, cage.layer->lower_layer);
precomputed.set_precomputed_layers({precompute_overhang_layer(cage.object, *cage.layer)});
precomputed.prepare_for_new_layer(cage.object, cage.layer);
check_identical(estimate_walls(precomputed, cage.object, *cage.layer), expected);
}
TEST_CASE("Overhang distances measured against another layer than the previous one are not used", "[ExtrusionProcessor]")
{
const SlicedCage cage;
const Layer *two_below = cage.layer->lower_layer->lower_layer;
REQUIRE(two_below != nullptr);
ExtrusionQualityEstimator queried;
queried.prepare_for_new_layer(cage.object, two_below);
queried.prepare_for_new_layer(cage.object, cage.layer);
const Walls expected = estimate_walls(queried, cage.object, *cage.layer);
ExtrusionQualityEstimator one_below;
one_below.prepare_for_new_layer(cage.object, cage.layer->lower_layer);
one_below.prepare_for_new_layer(cage.object, cage.layer);
REQUIRE(any_difference(estimate_walls(one_below, cage.object, *cage.layer), expected));
ExtrusionQualityEstimator precomputed;
precomputed.prepare_for_new_layer(cage.object, two_below);
precomputed.set_precomputed_layers({precompute_overhang_layer(cage.object, *cage.layer)});
precomputed.prepare_for_new_layer(cage.object, cage.layer);
check_identical(estimate_walls(precomputed, cage.object, *cage.layer), expected);
}
TEST_CASE("Overhang data computed for another layer is not used", "[ExtrusionProcessor]")
{
const SlicedCage cage;
const Layer *one_below = cage.layer->lower_layer;
REQUIRE(one_below != nullptr);
REQUIRE(one_below->lower_layer != nullptr);
ExtrusionQualityEstimator queried;
queried.prepare_for_new_layer(cage.object, one_below);
queried.prepare_for_new_layer(cage.object, cage.layer);
const Walls expected = estimate_walls(queried, cage.object, *cage.layer);
ExtrusionQualityEstimator two_below;
two_below.prepare_for_new_layer(cage.object, one_below->lower_layer);
two_below.prepare_for_new_layer(cage.object, cage.layer);
REQUIRE(any_difference(estimate_walls(two_below, cage.object, *cage.layer), expected));
ExtrusionQualityEstimator precomputed;
precomputed.set_precomputed_layers({precompute_overhang_layer(cage.object, *one_below)});
precomputed.prepare_for_new_layer(cage.object, one_below);
precomputed.prepare_for_new_layer(cage.object, cage.layer);
check_identical(estimate_walls(precomputed, cage.object, *cage.layer), expected);
}
TEST_CASE("Precomputed overhang data has the curled-line tree exactly when a region slows down for curled perimeters", "[ExtrusionProcessor]")
{
const bool slowdown = GENERATE(false, true);
DynamicPrintConfig config = caged_overhang_config("classic");
config.set_deserialize_strict("slowdown_for_curled_perimeters", slowdown ? "1" : "0");
const SlicedCage cage(config);
GCode::LayerToPrint layer;
layer.object_layer = cage.layer;
layer.original_object = cage.object;
const std::vector<PrecomputedOverhangLayer> precomputed = precompute_overhang_layers({layer}, false);
REQUIRE(precomputed.size() == 1);
CHECK((precomputed.front().lower_curled_lines != nullptr) == slowdown);
}
TEST_CASE("Caged external overhangs are slowed when printed by object or through the pressure equalizer", "[ExtrusionProcessor]")
{
const auto [key, value] = GENERATE(table<const char *, const char *>({
{"print_sequence", "by object"},
{"max_volumetric_extrusion_rate_slope", "10"},
}));
INFO(key << " = " << value);
DynamicPrintConfig config = caged_overhang_config("classic");
config.set_deserialize_strict(key, value);
Print print;
Model model;
init_print(std::vector<TriangleMesh>{caged_overhang_mesh()}, print, model, config, nullptr, false);
const std::vector<double> feed_rates = caged_slope_feed_rates(gcode(print));
info_feed_rates("caged slope", feed_rates);
REQUIRE_FALSE(feed_rates.empty());
REQUIRE(*std::max_element(feed_rates.begin(), feed_rates.end()) < caged_slow_speed * MM_PER_MIN);
}
TEST_CASE("Benchmark caged overhang interior sampling", "[ExtrusionProcessor][!benchmark]"){
const char* wall_generator = GENERATE("classic", "arachne");
+96 -11
View File
@@ -2,13 +2,35 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include "libslic3r/Fill/FillBase.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/Surface.hpp"
#include "libslic3r/Point.hpp"
#include <iterator>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Line.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <limits>
#include "libslic3r/Model.hpp"
#include <map>
#include <memory>
#include <math.h>
#include <numeric>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/AABBTreeLines.hpp"
#include "libslic3r/Fill/Fill.hpp"
@@ -748,6 +770,61 @@ TEST_CASE("A region with ironing turned off is never ironed", "[Fill]")
REQUIRE(Layer::choose_ironing_extruder(cfg, spiral_mode, /*is_topmost_layer=*/true) == -1);
}
// Ironing path count and total length in mm, over the whole object.
static std::pair<size_t, double> ironing_extent(const Print &print)
{
size_t paths = 0;
double length = 0.;
for (const Layer *layer : print.objects().front()->layers())
for (const LayerRegion *region : layer->regions())
for (const ExtrusionEntity *entity : region->fills.flatten().entities)
if (ironing_role(entity->role())) {
++paths;
length += unscale<double>(entity->length());
}
return {paths, length};
}
TEST_CASE("Ironing spacing below the minimum irons at the minimum spacing", "[Fill]")
{
const std::string pattern = GENERATE("rectilinear", "concentric");
const bool via_filament = GENERATE(false, true);
const double spacing = GENERATE(0., 0.001);
CAPTURE(pattern, via_filament, spacing);
auto ironing_for = [&pattern, via_filament](double spacing) {
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({{"ironing_type", "top"},
{"ironing_pattern", pattern},
{"layer_height", 0.2}});
// The filament override replaces the process spacing, which stays at a usable value.
if (via_filament)
config.set_deserialize_strict({{"ironing_spacing", 0.1}, {"filament_ironing_spacing", spacing}});
else
config.set_deserialize_strict({{"ironing_spacing", spacing}});
Print print;
Slic3r::Test::init_and_process_print({Slic3r::Test::cube(20)}, print, config);
return ironing_extent(print);
};
const std::pair<size_t, double> clamped = ironing_for(spacing);
const std::pair<size_t, double> minimum = ironing_for(IRONING_SPACING_MIN);
REQUIRE(minimum.first > 0);
CHECK(clamped.first == minimum.first);
CHECK_THAT(clamped.second, Catch::Matchers::WithinRel(minimum.second, 1e-9));
}
TEST_CASE("Concentric fill at zero spacing returns without paths", "[Fill]")
{
std::unique_ptr<Fill> filler(Fill::new_from_type(ipConcentric));
filler->spacing = 0.;
filler->bounding_box = BoundingBox(Point(0, 0), Point::new_scale(10, 10));
FillParams params;
params.density = 1.f;
Surface surface(stTop, ExPolygon({Point(0, 0), Point::new_scale(10, 0), Point::new_scale(10, 10), Point::new_scale(0, 10)}));
CHECK(filler->fill_surface(&surface, params).empty());
}
TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]")
{
auto angles_for = [](int direction) {
@@ -1616,24 +1693,32 @@ TEST_CASE("Smoothing multiline lightning infill keeps its outlines connected", "
// and the outlines of branches that run close to each other merge into one. Rounding the branches
// before those outlines are built moves them apart, which breaks the merged outlines up into
// separate loops - many more of them, each needing its own travel move.
// A micron change of the cube moves the loop count of a single slice by several percent, so the
// shapes of a few nearly equal cubes are added up.
auto shape_for = [](const std::string &smooth_factor) {
Print print;
Slic3r::Test::init_and_process_print({Slic3r::Test::cube(20)}, print,
{{"sparse_infill_pattern", "lightning"},
{"sparse_infill_density", "50%"},
{"fill_multiline", 2},
{"sparse_infill_smooth_factor", smooth_factor},
{"layer_height", 0.2}});
return sparse_infill_shape(print);
SparseInfillShape sum;
for (const double size : {20., 20.001, 20.002, 20.003}) {
Print print;
Slic3r::Test::init_and_process_print({Slic3r::Test::cube(size)}, print,
{{"sparse_infill_pattern", "lightning"},
{"sparse_infill_density", "50%"},
{"fill_multiline", 2},
{"sparse_infill_smooth_factor", smooth_factor},
{"layer_height", 0.2}});
const SparseInfillShape shape = sparse_infill_shape(print);
sum.path_count += shape.path_count;
sum.point_count += shape.point_count;
sum.sharp_turns += shape.sharp_turns;
}
return sum;
};
const SparseInfillShape sharp = shape_for("0%");
const SparseInfillShape smooth = shape_for("100%");
REQUIRE(sharp.path_count > 0);
// The loop count varies by a loop or two between platforms and between runs, so this is not an
// exact comparison. Smoothing should leave it about where it was; uncapping the smoothing
// reach, the regression this guards against, adds about 10%.
// Smoothing should leave the loop count about where it was; uncapping the smoothing reach, the
// regression this guards against, adds about 10%.
const size_t allowed_extra = sharp.path_count / 50; // 2%
REQUIRE(smooth.path_count <= sharp.path_count + allowed_extra);
// The outlines are still rounded.
+3
View File
@@ -1,8 +1,11 @@
#include <catch2/catch_all.hpp>
#include <cmath>
#include <numeric>
#include <sstream>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "test_helpers.hpp" // get access to init_print, etc
#include "libslic3r/Config.hpp"
+8
View File
@@ -1,5 +1,9 @@
#include <catch2/catch_all.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "libslic3r/MultiNozzleUtils.hpp"
@@ -8,9 +12,13 @@
#include "test_utils.hpp"
#include <cmath>
#include <cstddef>
#include <fstream>
#include <iomanip>
#include "libslic3r/ExtrusionEntity.hpp"
#include <ios>
#include <map>
#include <math.h>
#include <memory>
#include <sstream>
#include <string>
+2
View File
@@ -1,5 +1,7 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/GCode/GCodeProcessor.hpp"
+77 -4
View File
@@ -1,21 +1,38 @@
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_all.hpp>
#include <algorithm>
#include <cstdlib>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/Extruder.hpp"
#include "libslic3r/libslic3r.h"
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "nlohmann/json.hpp"
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/GCodeWriter.hpp"
#include "libslic3r/GCode.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/ModelArrange.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include "test_helpers.hpp"
@@ -23,10 +40,7 @@
using namespace Slic3r;
using namespace Slic3r::Test;
// Arrange on a finite bed, not an unbounded InfiniteBed: the latter places items
// near INT64_MIN/4 (~2.3e18), which reaches ClipperLib's coordinate limit and throws
// "Coordinate outside allowed range" on Windows/arm64. A 500x500 bed keeps coordinates
// small while still covering large printers.
// Arrange on a 500x500 bed, which keeps coordinates small while still covering large printers.
static void arrange_objects_on_test_bed(Model &model, const DynamicPrintConfig &config)
{
const BoundingBox bed{Point::new_scale(0., 0.), Point::new_scale(500., 500.)};
@@ -100,6 +114,22 @@ SCENARIO("Origin manipulation", "[GCodeWriter]") {
}
}
TEST_CASE("A cached config slot is looked up again whenever its key changes", "[GCodeWriter]")
{
GCode::ConfigIndexCache cache;
int lookups = 0;
auto slot = [&](int filament, size_t layer, size_t generation) {
return cache.get(filament, layer, generation, [&] { ++lookups; return filament * 100 + int(layer) * 10 + int(generation); });
};
REQUIRE(slot(1, 2, 3) == 123);
REQUIRE(slot(1, 2, 3) == 123);
REQUIRE(lookups == 1);
REQUIRE(slot(4, 2, 3) == 423);
REQUIRE(slot(4, 5, 3) == 453);
REQUIRE(slot(4, 5, 6) == 456);
REQUIRE(lookups == 4);
}
// Verify that emit_machine_limits_to_gcode emits the correct max value across
// used extruders (regression for commit b4ee665: "Emit max value of machine
// limit among used extruders").
@@ -875,3 +905,46 @@ TEST_CASE("Custom G-code motion limits are restored before generated moves", "[G
REQUIRE(gcode.find("M204 S6000 ; adjust acceleration", custom_gcode_pos) != std::string::npos);
REQUIRE(gcode.find("M205 X8 Y8 ; adjust jerk", custom_gcode_pos) != std::string::npos);
}
TEST_CASE("Percent accelerations resolve against the option they are a percentage of", "[GCodeWriter]")
{
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "gcode_flavor", "marlin" },
{ "machine_max_acceleration_extruding", "20000,20000" },
{ "default_acceleration", "4000" },
{ "initial_layer_acceleration", "0" },
{ "outer_wall_acceleration", "3000" },
{ "bridge_acceleration", "50%" },
{ "sparse_infill_acceleration", "25%" },
{ "internal_solid_infill_acceleration", "60%" },
{ "sparse_infill_density", "20%" },
});
// get_abs_value_at() resolves each percentage through the ratio_over in the config definitions.
const std::map<std::string, int> expected = {
{ "Bridge", int(config.get_abs_value_at("bridge_acceleration", 0)) },
{ "Sparse infill", int(config.get_abs_value_at("sparse_infill_acceleration", 0)) },
{ "Internal solid infill", int(config.get_abs_value_at("internal_solid_infill_acceleration", 0)) },
};
REQUIRE(expected.at("Bridge") == 1500);
REQUIRE(expected.at("Sparse infill") == 1000);
REQUIRE(expected.at("Internal solid infill") == 2400);
std::map<std::string, std::set<int>> accelerations_by_role;
std::string role;
int acceleration = 0;
GCodeReader reader;
reader.parse_buffer(Slic3r::Test::slice({ TestMesh::bridge }, config), [&](GCodeReader &self, const GCodeReader::GCodeLine &line) {
float value;
if (boost::starts_with(line.raw(), ";TYPE:"))
role = line.raw().substr(6);
else if (line.cmd_is("M204") && line.has_value('S', value))
acceleration = int(value);
else if (line.extruding(self) && line.dist_XY(self) > 0)
accelerations_by_role[role].insert(acceleration);
});
for (const auto &[role_name, value] : expected) {
INFO(role_name);
REQUIRE(accelerations_by_role[role_name] == std::set<int>{ value });
}
}
+16 -2
View File
@@ -1,5 +1,6 @@
#include "test_helpers.hpp"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Config.hpp"
@@ -7,11 +8,23 @@
#include "libslic3r/Format/OBJ.hpp"
#include "libslic3r/Format/STL.hpp"
#include <algorithm>
#include <cstdlib>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Exception.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <initializer_list>
#include "libslic3r/Point.hpp"
#include <fstream>
#include <iterator>
#include <set>
#include <string>
#include <boost/filesystem.hpp>
#include <libslic3r/ModelArrange.hpp>
#include <unordered_map>
#include <utility>
#include <vector>
#include "test_utils.hpp"
@@ -225,7 +238,7 @@ DynamicPrintConfig multifilament_config(unsigned int filaments, std::initializer
}
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model &model, const DynamicPrintConfig &config_in,
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> *per_object_overrides, bool arrange)
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> *per_object_overrides, bool arrange, size_t instances)
{
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.apply(config_in);
@@ -236,7 +249,8 @@ void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r
ModelObject *object = model.add_object();
object->name += "object.stl";
object->add_volume(std::move(t));
object->add_instance();
for (size_t i = 0; i < instances; ++i)
object->add_instance();
if (per_object_overrides && object_idx < per_object_overrides->size() && !(*per_object_overrides)[object_idx].empty()) {
DynamicPrintConfig oc;
+8 -2
View File
@@ -8,6 +8,10 @@
#include "libslic3r/Print.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/libslic3r.h"
#include <cstddef>
#include "libslic3r/PrintConfig.hpp"
#include <initializer_list>
#include <set>
#include <string>
#include <unordered_map>
@@ -72,9 +76,11 @@ Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
DynamicPrintConfig multifilament_config(unsigned int filaments,
std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> extra = {});
// Apply `meshes` and config to `print`/`model`; optional per-object overrides, auto-arranged unless `arrange` is false.
// Apply `meshes` and config to `print`/`model`, each object with `instances` copies; optional per-object overrides,
// auto-arranged unless `arrange` is false.
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model &model, const DynamicPrintConfig &config_in,
const std::vector<std::vector<Slic3r::ConfigBase::SetDeserializeItem>> *per_object_overrides = nullptr, bool arrange = true);
const std::vector<std::vector<Slic3r::ConfigBase::SetDeserializeItem>> *per_object_overrides = nullptr, bool arrange = true,
size_t instances = 1);
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config());
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config());
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items);
+11
View File
@@ -1,5 +1,16 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include "libslic3r/PrintConfig.hpp"
#include <string>
#include <cstddef>
#include "libslic3r/Model.hpp"
#include <vector>
#include "libslic3r/Config.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/PrintBase.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/GCode/ToolOrdering.hpp"
#include "libslic3r/MultiNozzleUtils.hpp"
#include "libslic3r/Print.hpp"
+7
View File
@@ -1,10 +1,17 @@
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Model.hpp"
#include "libslic3r/ModelArrange.hpp"
#include <boost/filesystem.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <vector>
#include "libslic3r/Point.hpp"
#include <cstddef>
#include "test_helpers.hpp"
#include "test_utils.hpp"
+8
View File
@@ -1,5 +1,8 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "libslic3r/GCodeReader.hpp"
@@ -12,6 +15,11 @@
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Print.hpp"
#include <limits>
#include <optional>
#include <set>
+12
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include "libslic3r/Layer.hpp"
@@ -10,8 +15,15 @@
#include <algorithm>
#include <cmath>
#include "libslic3r/PrintConfig.hpp"
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/Point.hpp"
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "test_helpers.hpp"
+15
View File
@@ -1,9 +1,24 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "test_helpers.hpp"
#include "libslic3r/GCode/PreciseSeam.hpp"
#include <algorithm>
#include "libslic3r/Point.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Layer.hpp"
#include <vector>
#include <utility>
#include "libslic3r/libslic3r.h"
#include "libslic3r/GCode/SeamPlacer.hpp"
#include <cstddef>
using namespace Slic3r;
+48
View File
@@ -8,6 +8,20 @@
#include <Windows.h>
#endif
#include <catch2/catch_test_macros.hpp>
#include <string>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/PrintConfig.hpp"
#include <cstddef>
#include "libslic3r/Surface.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintBase.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_all.hpp>
#include "libslic3r/libslic3r.h"
@@ -561,6 +575,40 @@ TEST_CASE("export_gcode writes G-code without a result pointer", "[Print][export
REQUIRE_FALSE(gcode.empty());
}
TEST_CASE("Exporting a sliced print again gives the same G-code", "[Print][export_gcode][Regression]")
{
const int instances = GENERATE(1, 3);
CAPTURE(instances);
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
TestMesh mesh = TestMesh::ipadstand;
SECTION("infill reversed by chaining") { config.set_deserialize_strict({{"sparse_infill_pattern", "gyroid"}}); }
SECTION("support reversed by chaining") {
mesh = TestMesh::overhang;
config.set_deserialize_strict({{"enable_support", true}, {"support_interface_pattern", "concentric"}});
}
Print print;
Model model;
Slic3r::Test::init_print({Slic3r::Test::mesh(mesh)}, print, model, config, nullptr, true, instances);
const auto export_without_timestamp = [&print]() {
std::string gcode = Slic3r::Test::gcode(print);
const size_t line = gcode.find("; generated by ");
REQUIRE(line != std::string::npos);
gcode.erase(line, gcode.find('\n', line) - line);
return gcode;
};
const std::string first = export_without_timestamp();
const std::string second = export_without_timestamp();
// Shows the first differing line on failure.
const size_t diff = std::mismatch(first.begin(), first.end(), second.begin(), second.end()).first - first.begin();
const size_t line_start = diff == 0 ? 0 : first.rfind('\n', diff - 1) + 1;
INFO("first export: " << first.substr(line_start, first.find('\n', diff) - line_start));
INFO("second export: " << second.substr(line_start, second.find('\n', diff) - line_start));
CHECK(diff == first.size());
CHECK(first.size() == second.size());
}
TEST_CASE("Sequential printing follows model order", "[Print]")
{
// Two objects of different heights, taller one added first. Orca prints
+19
View File
@@ -1,5 +1,11 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Print.hpp"
#include "libslic3r/Layer.hpp"
@@ -10,7 +16,20 @@
#include "test_helpers.hpp"
#include <cmath>
#include <cstddef>
#include <iterator>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Surface.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/Line.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <map>
#include <set>
#include <string>
+15
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "test_helpers.hpp"
#include "libslic3r/GCode/SeamPlacer.hpp"
#include "libslic3r/Layer.hpp"
@@ -7,6 +12,16 @@
#include <algorithm>
#include <cmath>
#include "libslic3r/Model.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <utility>
#include <cstddef>
#include "libslic3r/Point.hpp"
#include <vector>
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/libslic3r.h"
using namespace Slic3r;
+16
View File
@@ -1,5 +1,11 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/Geometry.hpp"
@@ -9,6 +15,16 @@
#include <boost/algorithm/string.hpp>
#include <cmath>
#include <cstddef>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Polyline.hpp"
#include <string>
#include "libslic3r/Model.hpp"
#include <vector>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/libslic3r.h"
#include <string_view>
#include "test_helpers.hpp" // get access to init_print, etc
@@ -1,4 +1,14 @@
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Config.hpp"
#include "libslic3r/Model.hpp"
#include <cstddef>
#include <string>
#include "libslic3r/Surface.hpp"
#include <utility>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Point.hpp"
#include <catch2/matchers/catch_matchers.hpp>
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "test_helpers.hpp"
using namespace Slic3r;
+16
View File
@@ -1,12 +1,28 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/Layer.hpp"
#include <cmath>
#include <cstddef>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/PrintBase.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/libslic3r.h"
#include "libslic3r/PrintConfig.hpp"
#include <map>
#include <math.h>
#include <mutex>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "test_helpers.hpp" // get access to init_print, etc
@@ -1,10 +1,22 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_all.hpp>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <ios>
#include <iterator>
#include <boost/filesystem.hpp>
#include <memory>
#include <vector>
#include <utility>
#include <string>
#include "libslic3r/PrintConfig.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/Model.hpp"
#include "libslic3r/PNGReadWrite.hpp"
#include "libslic3r/Print.hpp"
+8
View File
@@ -1,7 +1,15 @@
#include <catch2/catch_all.hpp>
#include <algorithm>
#include <initializer_list>
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Point.hpp"
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/Layer.hpp"
#include "libslic3r/TriangleMesh.hpp"
+7
View File
@@ -1,5 +1,6 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/Point.hpp"
@@ -8,8 +9,14 @@
#include "libslic3r/libslic3r.h"
#include <algorithm>
#include <cstdlib>
#include <cstddef>
#include <cmath>
#include <future>
#include <chrono>
#include <vector>
#include <math.h>
#include "libslic3r/ExPolygon.hpp"
//#include "test_options.hpp"
#include "test_helpers.hpp"
+16
View File
@@ -2,11 +2,27 @@
#include <algorithm>
#include <cmath>
#include "libslic3r/Point.hpp"
#include "libslic3r/libslic3r.h"
#include <cstddef>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/calib.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include <map>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "libslic3r/GCodeReader.hpp"
#include "libslic3r/PrintConfig.hpp"
+15
View File
@@ -1,8 +1,23 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include "libslic3r/Config.hpp"
#include "libslic3r/libslic3r.h"
#include "libslic3r/Point.hpp"
#include "libslic3r/Polygon.hpp"
#include <cstddef>
#include "libslic3r/Model.hpp"
#include "libslic3r/Print.hpp"
#include <string>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/GCode/GCodeProcessor.hpp"
@@ -2,13 +2,18 @@
#define FG_TEST_EVALUATOR_HPP
#include "fg_test_serialization.hpp"
#include <cstddef>
#include <libslic3r/FilamentGroup.hpp>
#include <libslic3r/GCode/ToolOrderUtils.hpp>
#include <libslic3r/MultiNozzleUtils.hpp>
#include <chrono>
#include <map>
#include <ratio>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>
namespace Slic3r {
namespace FGTest {
@@ -1,6 +1,7 @@
#ifndef FG_TEST_SERIALIZATION_HPP
#define FG_TEST_SERIALIZATION_HPP
#include <cstdio>
#include <nlohmann/json.hpp>
#include <libslic3r/FilamentGroup.hpp>
#include <libslic3r/FilamentGroupUtils.hpp>
@@ -10,6 +11,7 @@
#include <fstream>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <set>
#include <map>
+7
View File
@@ -2,9 +2,16 @@
#define FG_TEST_UTILS_HPP
#include "fg_test_serialization.hpp"
#include "libslic3r/FilamentGroupUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/FilamentGroup.hpp"
#include <iterator>
#include <random>
#include <algorithm>
#include <cassert>
#include <vector>
#include <set>
#include <string>
namespace Slic3r {
namespace FGTest {
@@ -11,13 +11,19 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include "fg_test_serialization.hpp"
#include "fg_test_evaluator.hpp"
#include "fg_test_utils.hpp"
#include <cstddef>
#include <filesystem>
#include <iostream>
#include <fstream>
#include "libslic3r/FilamentGroup.hpp"
#include <string>
#include <vector>
#include <algorithm>
+3
View File
@@ -8,7 +8,10 @@
// it here and include this header from every libnest2d test file.
#include <cstdint>
#include <iostream>
#include "libnest2d/common.hpp"
#include "libnest2d/geometry_traits_nfp.hpp"
#include <libnest2d/libnest2d.hpp>
#include <libnest2d/utils/rotcalipers.hpp>
+5
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include "libnest2d/libnest2d.hpp"
#include <vector>
#include <cstddef>
#include "libnest2d/nester.hpp"
#include <catch2/catch_test_macros.hpp>
#include "libnest2d_test_utils.hpp"
using namespace libnest2d;
+1
View File
@@ -1,6 +1,7 @@
#ifndef PRINTER_PARTS_H
#define PRINTER_PARTS_H
#include "libnest2d/backends/libslic3r/geometries.hpp"
#include <vector>
#include <libnest2d/libnest2d.hpp>
+19
View File
@@ -1,5 +1,24 @@
#include <algorithm>
#include <boost/rational.hpp>
#include <boost/multiprecision/fwd.hpp>
#include <catch2/catch_all.hpp>
#include <vector>
#include "libnest2d/libnest2d.hpp"
#include "libnest2d/geometry_traits.hpp"
#include "libnest2d/backends/libslic3r/geometries.hpp"
#include "libnest2d/common.hpp"
#include <type_traits>
#include <cstdlib>
#include "libnest2d/placers/nfpplacer.hpp"
#include <cmath>
#include <cstdint>
#include <limits>
#include <iterator>
#include "libnest2d/utils/rotcalipers.hpp"
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libnest2d_test_utils.hpp"
#include "printer_parts.hpp"
+12
View File
@@ -1,5 +1,17 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include "libnest2d/libnest2d.hpp"
#include <vector>
#include "libnest2d/geometry_traits.hpp"
#include "libnest2d/backends/libslic3r/geometries.hpp"
#include "libnest2d/geometry_traits_nfp.hpp"
#include "libnest2d/utils/boost_alg.hpp"
#include "libnest2d/placers/nfpplacer.hpp"
#include <iterator>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libnest2d_test_utils.hpp"
using namespace libnest2d;
+8
View File
@@ -1,5 +1,13 @@
#include <catch2/catch_all.hpp>
#include "libnest2d/libnest2d.hpp"
#include <vector>
#include <cstddef>
#include "libnest2d/geometry_traits.hpp"
#include "libnest2d/common.hpp"
#include <catch2/catch_message.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libnest2d_test_utils.hpp"
using namespace libnest2d;
+2
View File
@@ -42,6 +42,7 @@ add_executable(${_TEST_NAME}_tests
test_stl.cpp
test_triangle_selector.cpp
test_meshboolean.cpp
test_trianglemesh_slicer.cpp
test_marchingsquares.cpp
test_lay_on_face.cpp
test_model.cpp
@@ -57,6 +58,7 @@ add_executable(${_TEST_NAME}_tests
# test_png_io.cpp
test_indexed_triangle_set.cpp
test_texture_displacement.cpp
test_instance_lock.cpp
../libnest2d/printer_parts.cpp
)
+5
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <vector>
#include "libslic3r/libslic3r.h"
#include <string>
#include <cstdio>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Utils.hpp"
#define NANOSVG_IMPLEMENTATION
#include "nanosvg/nanosvg.h"
+18
View File
@@ -1,4 +1,9 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/Model.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "libslic3r/Format/bbs_3mf.hpp"
@@ -14,6 +19,15 @@
#include "test_utils.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/Geometry.hpp"
#include <cstddef>
#include <miniz.h>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleSelector.hpp"
#include <memory>
#include <cstdint>
#include <ios>
#include <nlohmann/json.hpp>
#include <boost/filesystem/operations.hpp>
@@ -25,8 +39,12 @@
#include <catch2/catch_tostring.hpp>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <string>
#include <sstream>
#include <type_traits> // for std::enable_if_t
#include <typeinfo> // for typeid
#include <vector>
#include <utility>
namespace Catch {
template <typename T>
+5
View File
@@ -1,8 +1,13 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "test_utils.hpp"
#include <igl/Hit.h>
#include <cstddef>
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/AABBTreeIndirect.hpp>
#include <vector>
using namespace Slic3r;
+1
View File
@@ -1,5 +1,6 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/AppConfig.hpp"
using namespace Slic3r;
+11
View File
@@ -17,6 +17,9 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/Arachne/WallToolPaths.hpp"
#include "libslic3r/Arachne/SkeletalTrapezoidation.hpp"
#include "libslic3r/Arachne/utils/ExtrusionLine.hpp"
@@ -31,6 +34,14 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include <vector>
#include <utility>
#include <math.h>
#include "libslic3r/Arachne/utils/ExtrusionJunction.hpp"
#include "libslic3r/PerimeterGenerator.hpp"
#include "libslic3r/PrintConfig.hpp"
using namespace Slic3r;
using namespace Slic3r::Arachne;
+14
View File
@@ -1,5 +1,19 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Point.hpp"
#include <string>
#include "libslic3r/Config.hpp"
#include <vector>
#include <limits>
#include <cstddef>
#include <cmath>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/Arrange.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/ClipperUtils.hpp"
@@ -1,5 +1,6 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "slic3r/Utils/bambu_networking.hpp"
using namespace Slic3r;
+5
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <vector>
#include "libslic3r/Point.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/BuildVolume.hpp"
using namespace Slic3r;
+19
View File
@@ -1,4 +1,23 @@
#include <catch2/catch_message.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <Standard_TypeDef.hxx>
#include <algorithm>
#include <TopAbs_ShapeEnum.hxx>
#include <GeomAbs_CurveType.hxx>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include <vector>
#include <math.h>
#include <utility>
#include <sstream>
#include <cstdint>
#include <ios>
#include <iterator>
#include "libslic3r/TriangleMesh.hpp"
#include <exception>
#include <string>
// Substring assertions, spelled so this file compiles UNCHANGED on both forks.
// Catch2 v2 (Snapmaker) spells it Matchers::Contains; v3 (orca_cad / mainline) spells it
+4
View File
@@ -1,9 +1,13 @@
#include <catch2/catch_all.hpp>
#include <algorithm>
#include "libslic3r/CustomGCode.hpp"
#include <sstream>
#include <string>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/calib.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleMesh.hpp"
+12 -5
View File
@@ -1,8 +1,15 @@
#include <catch2/catch_all.hpp>
#include <cmath>
#include <iostream>
#include <boost/filesystem.hpp>
#include "libslic3r/libslic3r.h"
#include <vector>
#include "libslic3r/Polygon.hpp"
#include <math.h>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/SVG.hpp"
@@ -22,7 +29,7 @@ SCENARIO("Constant offset", "[ClipperUtils]") {
WHEN("Slic3r::offset()") {
for (double miter : { 2.0, 1.5, 1.2 }) {
DYNAMIC_SECTION("plus 1mm, miter " << miter << "x") {
output = Slic3r::offset(box20mm, 1. * s, ClipperLib::jtMiter, miter);
output = Slic3r::offset(box20mm, 1. * s, jtMiter, miter);
#ifdef TESTS_EXPORT_SVGS
{
SVG svg(debug_out_path("constant_offset_box20mm_plus1mm_miter%lf.svg", miter).c_str(), get_extents(output));
@@ -36,7 +43,7 @@ SCENARIO("Constant offset", "[ClipperUtils]") {
}
}
DYNAMIC_SECTION("minus 1mm, miter " << miter << "x") {
output = Slic3r::offset(box20mm, - 1. * s, ClipperLib::jtMiter, miter);
output = Slic3r::offset(box20mm, - 1. * s, jtMiter, miter);
#ifdef TESTS_EXPORT_SVGS
{
SVG svg(debug_out_path("constant_offset_box20mm_minus1mm_miter%lf.svg", miter).c_str(), get_extents(output));
@@ -96,7 +103,7 @@ SCENARIO("Constant offset", "[ClipperUtils]") {
for (double miter : { 2.0, 1.5, 1.2 }) {
DYNAMIC_SECTION("miter " << miter << "x") {
WHEN("plus 1mm") {
output = Slic3r::offset_ex(box20mm, 1. * s, ClipperLib::jtMiter, miter);
output = Slic3r::offset_ex(box20mm, 1. * s, jtMiter, miter);
#ifdef TESTS_EXPORT_SVGS
{
SVG svg(debug_out_path("constant_offset_box20mm_10mm_hole_plus1mm_miter%lf.svg", miter).c_str(), get_extents(output));
@@ -110,7 +117,7 @@ SCENARIO("Constant offset", "[ClipperUtils]") {
}
}
WHEN("minus 1mm") {
output = Slic3r::offset_ex(box20mm, - 1. * s, ClipperLib::jtMiter, miter);
output = Slic3r::offset_ex(box20mm, - 1. * s, jtMiter, miter);
#ifdef TESTS_EXPORT_SVGS
{
SVG svg(debug_out_path("constant_offset_box20mm_10mm_hole_minus1mm_miter%lf.svg", miter).c_str(), get_extents(output));
@@ -176,7 +183,7 @@ SCENARIO("Constant offset", "[ClipperUtils]") {
SECTION("Slic3r::offset()") {
for (double miter : { 2.0, 1.5, 1.2 }) {
DYNAMIC_SECTION("Outer offset 1mm, miter " << miter << "x") {
output = Slic3r::offset(triangle20mm, offset * s, ClipperLib::jtMiter, 2.0);
output = Slic3r::offset(triangle20mm, offset * s, jtMiter, 2.0);
#ifdef TESTS_EXPORT_SVGS
{
SVG svg(debug_out_path("constant_offset_triangle20mm_plus1mm_miter%lf.svg", miter).c_str(), get_extents(output));
+49 -49
View File
@@ -1,15 +1,44 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Polyline.hpp"
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include <numeric>
#include <iostream>
#include <boost/filesystem.hpp>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/SVG.hpp"
using namespace Slic3r;
// Clipper may start an output polygon at any of its vertices.
static Polygon start_at_min(Polygon polygon)
{
std::rotate(polygon.points.begin(), std::min_element(polygon.points.begin(), polygon.points.end()), polygon.points.end());
return polygon;
}
static Polygons start_at_min(Polygons polygons)
{
for (Polygon &polygon : polygons)
polygon = start_at_min(std::move(polygon));
return polygons;
}
static ExPolygons start_at_min(ExPolygons expolygons)
{
for (ExPolygon &expolygon : expolygons) {
expolygon.contour = start_at_min(std::move(expolygon.contour));
expolygon.holes = start_at_min(std::move(expolygon.holes));
}
return expolygons;
}
SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
// CCW oriented contour
Slic3r::Polygon square{ { 200, 100 }, {200, 200}, {100, 200}, {100, 100} };
@@ -20,25 +49,25 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
WHEN("offset") {
Polygons result = Slic3r::offset(square_with_hole, 5.f);
THEN("offset matches") {
REQUIRE(result == Polygons {
REQUIRE(start_at_min(result) == start_at_min(Polygons {
{ { 205, 205 }, { 95, 205 }, { 95, 95 }, { 205, 95 }, },
{ { 155, 145 }, { 145, 145 }, { 145, 155 }, { 155, 155 } } });
{ { 155, 145 }, { 145, 145 }, { 145, 155 }, { 155, 155 } } }));
}
}
WHEN("offset_ex") {
ExPolygons result = Slic3r::offset_ex(square_with_hole, 5.f);
THEN("offset matches") {
REQUIRE(result == ExPolygons { {
REQUIRE(start_at_min(result) == start_at_min(ExPolygons { {
{ { 205, 205 }, { 95, 205 }, { 95, 95 }, { 205, 95 }, },
{ { 145, 145 }, { 145, 155 }, { 155, 155 }, { 155, 145 } } } } );
{ { 145, 145 }, { 145, 155 }, { 155, 155 }, { 155, 145 } } } }));
}
}
WHEN("offset2_ex") {
ExPolygons result = Slic3r::offset2_ex({ square_with_hole }, 5.f, -2.f);
THEN("offset matches") {
REQUIRE(result == ExPolygons { {
REQUIRE(start_at_min(result) == start_at_min(ExPolygons { {
{ { 203, 203 }, { 97, 203 }, { 97, 97 }, { 203, 97 } },
{ { 143, 143 }, { 143, 157 }, { 157, 157 }, { 157, 143 } } } } );
{ { 143, 143 }, { 143, 157 }, { 157, 157 }, { 157, 143 } } } }));
}
}
}
@@ -225,24 +254,7 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") {
}
}
template<e_ordering o = e_ordering::OFF, class P, class Tree, class Alloc>
double polytree_area(const Tree &tree, std::vector<P, Alloc> *out)
{
traverse_pt<o>(tree, out);
return std::accumulate(out->begin(), out->end(), 0.0,
[](double a, const P &p) { return a + p.area(); });
}
size_t count_polys(const ExPolygons& expolys)
{
size_t c = 0;
for (auto &ep : expolys) c += ep.holes.size() + 1;
return c;
}
TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") {
TEST_CASE("Top level expolygons of an even-odd union", "[ClipperUtils]") {
// Create a polygon representing unit box
Polygon unitbox;
const auto UNIT = coord_t(1. / SCALING_FACTOR);
@@ -266,36 +278,24 @@ TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") {
Polygon inner_right = inner_left;
inner_right.translate(UNIT * 10, 0);
Polygons reference = union_({box_frame, hole_left, hole_right, inner_left, inner_right});
ExPolygons reference;
for (const Polygon &polygon : union_({box_frame, hole_left, hole_right, inner_left, inner_right}))
reference.emplace_back(polygon);
ClipperLib::PolyTree tree = union_pt(reference);
double area_sum = box_frame.area() + hole_left.area() +
hole_right.area() + inner_left.area() +
inner_right.area();
REQUIRE(area_sum > 0);
SECTION("Traverse into Polygons WITHOUT spatial ordering") {
Polygons output;
REQUIRE(area_sum == Catch::Approx(polytree_area(tree.GetFirst(), &output)));
REQUIRE(output.size() == reference.size());
}
SECTION("Traverse into ExPolygons WITHOUT spatial ordering") {
ExPolygons output;
REQUIRE(area_sum == Catch::Approx(polytree_area(tree.GetFirst(), &output)));
REQUIRE(count_polys(output) == reference.size());
}
SECTION("Traverse into Polygons WITH spatial ordering") {
Polygons output;
REQUIRE(area_sum == Catch::Approx(polytree_area<e_ordering::ON>(tree.GetFirst(), &output)));
REQUIRE(output.size() == reference.size());
}
SECTION("Traverse into ExPolygons WITH spatial ordering") {
ExPolygons output;
REQUIRE(area_sum == Catch::Approx(polytree_area<e_ordering::ON>(tree.GetFirst(), &output)));
REQUIRE(count_polys(output) == reference.size());
}
ExPolygons nested;
ExPolygons top_level = top_level_expolygons(reference, &nested);
auto area = [](const ExPolygons &expolys) {
return std::accumulate(expolys.begin(), expolys.end(), 0.0, [](double a, const ExPolygon &e) { return a + e.area(); });
};
REQUIRE(top_level.size() == 1);
REQUIRE(top_level.front().holes.size() == 2);
REQUIRE(nested.size() == 2);
REQUIRE(area_sum == Catch::Approx(area(top_level) + area(nested)));
REQUIRE(top_level_expolygons(reference).size() == 1);
}
+115
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/PrintConfigConstants.hpp"
#include "libslic3r/LocalesUtils.hpp"
@@ -13,9 +18,16 @@
#include <boost/filesystem.hpp>
#include <boost/nowide/fstream.hpp>
#include "libslic3r/Config.hpp"
#include <memory>
#include <map>
#include <iterator>
#include <nlohmann/json.hpp>
#include <set>
#include <sstream>
#include <vector>
#include <utility>
using namespace Slic3r;
@@ -421,6 +433,109 @@ SCENARIO("update_diff_values_to_child_config tolerates legacy machine-limit vect
}
}
TEST_CASE("A variant index comes from the same variant and id, else the id's first variant", "[Config][Variant]") {
const std::vector<std::string> variant_list{"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard"};
const std::vector<int> variant_ids{1, 1, 2};
SECTION("same variant and id") {
CHECK(Slic3r::find_variant_index("Direct Drive High Flow", 1, variant_list, variant_ids) == 1);
CHECK(Slic3r::find_variant_index("Direct Drive Standard", 2, variant_list, variant_ids) == 2);
}
SECTION("a variant the id lacks falls back to the id's first variant") {
CHECK(Slic3r::find_variant_index("Bowden Standard", 1, variant_list, variant_ids) == 0);
CHECK(Slic3r::find_variant_index("Direct Drive High Flow", 2, variant_list, variant_ids) == 2);
}
SECTION("an id with no variants matches none") {
CHECK(Slic3r::find_variant_index("Direct Drive Standard", 3, variant_list, variant_ids) == -1);
}
SECTION("a negative id or a list without ids matches any id") {
CHECK(Slic3r::find_variant_index("Direct Drive High Flow", -1, variant_list, variant_ids) == 1);
CHECK(Slic3r::find_variant_index("Direct Drive High Flow", 2, variant_list, {}) == 1);
}
SECTION("a variant past a shorter id list gets no variant index") {
CHECK(Slic3r::map_variant_indices(variant_list, {1}, variant_list, variant_ids) == std::vector<int>{0, -1, -1});
}
SECTION("a list without variant strings has one variant per id, and an empty one a single variant") {
CHECK(Slic3r::map_variant_indices(variant_list, variant_ids, {}, {1, 2}) == std::vector<int>{0, 0, 1});
CHECK(Slic3r::map_variant_indices(variant_list, variant_ids, {}, {}) == std::vector<int>{0, 0, 0});
}
}
SCENARIO("update_diff_values_to_child_config keeps a child's values on variants it does not list",
"[Config][Variant]") {
std::set<std::string> no_keys;
auto variants = [](std::initializer_list<std::string> names) { return new Slic3r::ConfigOptionStrings(names); };
GIVEN("A filament parent with three variants") {
Slic3r::DynamicPrintConfig parent;
parent.set_key_value("filament_extruder_variant",
variants({"Direct Drive Standard", "Bowden Standard", "Direct Drive High Flow"}));
parent.set_deserialize_strict("nozzle_temperature", "220,220,220");
WHEN("the child was saved when the parent had only its first variant") {
Slic3r::DynamicPrintConfig child;
child.set_key_value("filament_extruder_variant", variants({"Direct Drive Standard"}));
child.set_deserialize_strict("nozzle_temperature", "199");
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("the child's value applies to every variant") {
REQUIRE(parent.opt_serialize("nozzle_temperature") == "199,199,199");
}
}
WHEN("the child lists every variant, in another order") {
Slic3r::DynamicPrintConfig child;
child.set_key_value("filament_extruder_variant",
variants({"Bowden Standard", "Direct Drive High Flow", "Direct Drive Standard"}));
child.set_deserialize_strict("nozzle_temperature", "190,205,199");
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("each variant keeps its own value") {
REQUIRE(parent.opt_serialize("nozzle_temperature") == "199,190,205");
}
}
WHEN("the child lists no variants") {
Slic3r::DynamicPrintConfig child;
child.set_deserialize_strict("nozzle_temperature", "199");
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("the child's value applies to every variant") {
REQUIRE(parent.opt_serialize("nozzle_temperature") == "199,199,199");
}
}
}
GIVEN("A two-extruder printer parent with two variants per extruder") {
Slic3r::DynamicPrintConfig parent;
parent.set_key_value("printer_extruder_variant",
variants({"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}));
parent.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1, 2, 2}));
parent.set_deserialize_strict("retraction_length", "0.8,0.8,0.8,0.8");
WHEN("the child lists only the Standard variant of each extruder") {
Slic3r::DynamicPrintConfig child;
child.set_key_value("printer_extruder_variant", variants({"Direct Drive Standard", "Direct Drive Standard"}));
child.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2}));
child.set_deserialize_strict("retraction_length", "1.1,2.2");
parent.update_diff_values_to_child_config(child, "printer_extruder_id", "printer_extruder_variant",
Slic3r::printer_options_with_variant_1,
Slic3r::printer_options_with_variant_2);
THEN("each extruder's High Flow variant takes that extruder's value") {
REQUIRE(parent.opt_serialize("retraction_length") == "1.1,1.1,2.2,2.2");
}
}
WHEN("the child lists no variants") {
Slic3r::DynamicPrintConfig child;
child.set_deserialize_strict("retraction_length", "1.1");
parent.update_diff_values_to_child_config(child, "printer_extruder_id", "printer_extruder_variant",
Slic3r::printer_options_with_variant_1,
Slic3r::printer_options_with_variant_2);
THEN("only the first extruder's variants take the child's value") {
REQUIRE(parent.opt_serialize("retraction_length") == "1.1,1.1,0.8,0.8");
}
}
}
}
// SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") {
// WHEN("DynamicPrintConfig is serialized and deserialized") {
// auto now = std::chrono::high_resolution_clock::now();
@@ -1,5 +1,15 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/Config.hpp"
#include <string>
#include <vector>
#include <set>
#include <cmath>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_approx.hpp>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Preset.hpp"
+9
View File
@@ -1,12 +1,21 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Format/DRC.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <boost/nowide/fstream.hpp>
#include <draco/attributes/geometry_attribute.h>
#include <cstdint>
#include <draco/attributes/geometry_indices.h>
#include <draco/compression/config/compression_shared.h>
#include <draco/compression/encode.h>
#include <draco/core/draco_types.h>
#include <draco/core/encoder_buffer.h>
#include <draco/mesh/mesh.h>
#include <string>
#include <ios>
#include "test_utils.hpp"
@@ -2,7 +2,11 @@
#include <iostream>
#include <boost/filesystem.hpp>
#include "libslic3r/Point.hpp"
#include <vector>
#include "libslic3r/libslic3r.h"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/ElephantFootCompensation.hpp"
#include "libslic3r/ExPolygon.hpp"
@@ -14,7 +18,7 @@ using namespace Slic3r;
// #define TESTS_EXPORT_SVGS
namespace Slic3r {
ClipperLib::Path mittered_offset_path_scaled(const Points& contour, const std::vector<float>& deltas, double miter_limit);
Points mittered_offset_path_scaled(const Points& contour, const std::vector<float>& deltas, double miter_limit);
}
static ExPolygon spirograph_gear_1mm()
+8
View File
@@ -1,5 +1,13 @@
#include <catch2/catch_all.hpp>
#include <vector>
#include <cstddef>
#include "libslic3r/Config.hpp"
#include <string>
#include <cstdlib>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/FilamentMixer.hpp"
#include "libslic3r/PrintConfig.hpp"
@@ -2,8 +2,12 @@
#include <algorithm>
#include <cmath>
#include "libslic3r/Point.hpp"
#include <cstddef>
#include "libslic3r/Polygon.hpp"
#include <limits>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Fill/FillCornerSmoothing.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/libslic3r.h"
+9
View File
@@ -2,9 +2,18 @@
#include <algorithm>
#include <cmath>
#include "libslic3r/Point.hpp"
#include "libslic3r/libslic3r.h"
#include "libslic3r/Fill/FillBase.hpp"
#include <cstddef>
#include <iterator>
#include "libslic3r/Config.hpp"
#include <limits>
#include <utility>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/Fill/FillPlanePath.hpp"
#include "libslic3r/PrintConfig.hpp"
+10
View File
@@ -1,5 +1,8 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Polygon.hpp"
@@ -17,7 +20,14 @@
#include "../libnest2d/printer_parts.hpp"
#include "libslic3r/libslic3r.h"
#include <cstddef>
#include <cstdlib>
#include <math.h>
#include <functional>
#include <string>
#include <unordered_set>
#include <vector>
using namespace Slic3r;
+3
View File
@@ -1,7 +1,10 @@
#include <iostream>
#include <fstream>
#include <catch2/catch_all.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/libslic3r.h"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/SLA/Hollowing.hpp"
#include "test_utils.hpp"
@@ -1,8 +1,17 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <fstream>
#include "libslic3r/Point.hpp"
#include "libslic3r/libslic3r.h"
#include <limits>
#include <random>
#include <catch2/catch_all.hpp>
#include <vector>
#include <string>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "test_utils.hpp"
+230
View File
@@ -0,0 +1,230 @@
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_all.hpp>
#include <atomic>
#include <chrono>
#include <thread>
#include <boost/filesystem.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/InstanceLock.hpp"
#include "test_utils.hpp"
#ifdef _WIN32
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/fstream.hpp>
#else
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#endif
using namespace Slic3r;
using namespace std::chrono_literals;
// Sets a process-wide knob for one test and restores it however the test ends.
template<typename T> struct ScopedStaticValue
{
T &ref;
T saved;
ScopedStaticValue(T &ref, T value) : ref(ref), saved(ref) { ref = value; }
~ScopedStaticValue() { ref = saved; }
};
TEST_CASE("InstanceLock creates its lock file and holds it for the guard's scope", "[InstanceLock]")
{
ScopedTemporaryFile lock_file(".lock");
const std::string path = lock_file.string();
{
InstanceLock lock(path);
REQUIRE(lock.locked());
REQUIRE(boost::filesystem::exists(path));
}
// Released: a fresh guard gets the lock at once instead of waiting out a timeout.
const auto started = std::chrono::steady_clock::now();
InstanceLock again(path, 5000ms);
REQUIRE(again.locked());
// Well inside the timeout it would otherwise have waited out; loose enough for a loaded runner.
REQUIRE(std::chrono::steady_clock::now() - started < 4000ms);
}
TEST_CASE("InstanceLock nests within one thread", "[InstanceLock]")
{
ScopedTemporaryFile lock_file(".lock");
const std::string path = lock_file.string();
InstanceLock outer(path);
{
InstanceLock inner(path, 100ms);
REQUIRE(inner.locked());
}
// The inner guard leaving does not release the outer one.
REQUIRE(outer.locked());
}
TEST_CASE("InstanceLock is a no-op for an empty path and survives an unwritable one", "[InstanceLock]")
{
ScopedTemporaryDir dir;
InstanceLock none("");
REQUIRE_FALSE(none.locked());
// The directory does not exist, so the lock file cannot be created; the
// guard still constructs and the write it guards can go ahead.
InstanceLock unwritable((dir.path() / "missing" / "shared.lock").string(), 100ms);
REQUIRE_FALSE(unwritable.locked());
}
TEST_CASE("InstanceLock retries a lock file it could not open once the cool-down passes", "[InstanceLock]")
{
ScopedTemporaryDir dir;
const std::string path = (dir.path() / "later" / "shared.lock").string();
ScopedStaticValue cooldown(InstanceLock::cooldown, 300ms);
bool before_dir, during_cooldown, after_cooldown;
const auto started = std::chrono::steady_clock::now();
{
InstanceLock lock(path, 100ms);
before_dir = lock.locked();
}
boost::filesystem::create_directories(dir.path() / "later");
{
InstanceLock lock(path, 100ms);
during_cooldown = lock.locked();
}
const bool second_guard_inside_cooldown = std::chrono::steady_clock::now() - started < InstanceLock::cooldown;
std::this_thread::sleep_for(400ms);
{
InstanceLock lock(path, 100ms);
after_cooldown = lock.locked();
}
REQUIRE_FALSE(before_dir);
// A loaded runner may take longer than the cool-down to get here; then the
// second guard legitimately retried, so only assert when the timing held.
if (second_guard_inside_cooldown)
REQUIRE_FALSE(during_cooldown);
REQUIRE(after_cooldown);
}
TEST_CASE("InstanceLock reopens a lock file that was replaced on disk", "[InstanceLock]")
{
ScopedTemporaryFile lock_file(".lock");
const std::string path = lock_file.string();
{
InstanceLock lock(path);
REQUIRE(lock.locked());
}
boost::filesystem::remove(path);
InstanceLock lock(path);
REQUIRE(lock.locked());
// Each outermost guard opens the file afresh, so the deleted path is back.
REQUIRE(boost::filesystem::exists(path));
}
TEST_CASE("InstanceLock serialises the threads of one process", "[InstanceLock]")
{
ScopedTemporaryFile lock_file(".lock");
const std::string path = lock_file.string();
std::atomic<bool> holder_ready{false};
std::atomic<bool> holder_released{false};
std::thread holder([&] {
InstanceLock lock(path);
holder_ready = true;
std::this_thread::sleep_for(150ms);
holder_released = true;
});
while (! holder_ready)
std::this_thread::yield();
bool released_before_acquire = false;
{
InstanceLock lock(path);
released_before_acquire = holder_released;
}
holder.join();
REQUIRE(released_before_acquire);
}
// Holds the OS lock on a file through a handle of its own, as another instance would. The lock
// belongs to the handle on Windows and to the open file description elsewhere, so the guard's
// handle is refused while this one holds it.
class OtherHolder
{
public:
explicit OtherHolder(const std::string &path)
{
#ifdef _WIN32
boost::nowide::ofstream(path, std::ios::app).close();
m_lock = boost::interprocess::file_lock(boost::nowide::widen(path).c_str());
m_held = m_lock.try_lock();
#else
m_fd = ::open(path.c_str(), O_RDWR | O_CREAT, 0644);
m_held = m_fd >= 0 && ::flock(m_fd, LOCK_EX | LOCK_NB) == 0;
#endif
}
~OtherHolder() { release(); }
OtherHolder(const OtherHolder &) = delete;
OtherHolder &operator=(const OtherHolder &) = delete;
bool held() const { return m_held; }
void release()
{
#ifdef _WIN32
if (m_held)
m_lock.unlock();
m_lock = boost::interprocess::file_lock();
#else
if (m_fd >= 0)
::close(m_fd);
m_fd = -1;
#endif
m_held = false;
}
private:
#ifdef _WIN32
boost::interprocess::file_lock m_lock;
#else
int m_fd{-1};
#endif
bool m_held{false};
};
TEST_CASE("InstanceLock yields to a lock held through another handle and reports it", "[InstanceLock]")
{
ScopedTemporaryFile lock_file(".lock");
const std::string path = lock_file.string();
ScopedStaticValue cooldown(InstanceLock::cooldown, 300ms);
OtherHolder other(path);
REQUIRE(other.held());
bool locked_while_other_holds;
{
InstanceLock lock(path, 100ms);
locked_while_other_holds = lock.locked();
}
// The timed-out wait starts a cool-down: the next guard does not touch the file.
const auto started = std::chrono::steady_clock::now();
bool locked_during_cooldown;
{
InstanceLock lock(path, 5000ms);
locked_during_cooldown = lock.locked();
}
const auto cooldown_wait = std::chrono::steady_clock::now() - started;
other.release();
REQUIRE_FALSE(locked_while_other_holds);
REQUIRE_FALSE(locked_during_cooldown);
REQUIRE(cooldown_wait < 4000ms);
// Once the cool-down passes, the lock the other holder released is taken again.
std::this_thread::sleep_for(400ms);
InstanceLock lock(path);
REQUIRE(lock.locked());
}
+10
View File
@@ -1,5 +1,15 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/Point.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <utility>
#include <vector>
#include "libslic3r/libslic3r.h"
#include <cstddef>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/LayOnFace.hpp"
#include "libslic3r/Model.hpp"
+2
View File
@@ -1,7 +1,9 @@
#include <catch2/catch_all.hpp>
#include <clocale>
#include <locale.h>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/LocalesUtils.hpp"
using namespace Slic3r;
+22
View File
@@ -2,6 +2,28 @@
#define NOMINMAX
#endif
#include "libslic3r/Point.hpp"
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include <utility>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Polygon.hpp"
#include <algorithm>
#include <iterator>
#include "libslic3r/SLA/RasterBase.hpp"
#include <agg/agg_gamma_functions.h>
#include <vector>
#include <cmath>
#include <string>
#include "libslic3r/ExPolygon.hpp"
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include <cstdlib>
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/catch_all.hpp>
#include "test_utils.hpp"
+2
View File
@@ -1,4 +1,6 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include "test_utils.hpp"
#include <libslic3r/TriangleMesh.hpp>
@@ -1,7 +1,11 @@
#include <catch2/catch_all.hpp>
#include <algorithm>
#include <vector>
#include <cstddef>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/MinimumSpanningTree.hpp"
#include "libslic3r/Point.hpp"
+10
View File
@@ -1,5 +1,12 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/file_status.hpp>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/miniz_extension.hpp"
#include "test_utils.hpp"
@@ -7,8 +14,11 @@
#include <boost/filesystem.hpp>
#include <algorithm>
#include <cstddef>
#include <fstream>
#include <ios>
#include <iterator>
#include <miniz.h>
#include <string>
#include <utility>
#include <vector>
+5
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Point.hpp"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Model.hpp"
using namespace Slic3r;
@@ -3,6 +3,12 @@
// MultiMaterialSegmentation.hpp declares boost::polygon traits for ColoredLine, so its
// geometry/boost dependencies must be included first.
#include <boost/polygon/polygon.hpp>
#include <catch2/catch_test_macros.hpp>
#include <string>
#include <vector>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/Line.hpp"
#include "libslic3r/Flow.hpp"
#include "libslic3r/MultiMaterialSegmentation.hpp"
+4
View File
@@ -1,5 +1,9 @@
#include <catch2/catch_all.hpp>
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Polygon.hpp"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Point.hpp"
#include "libslic3r/MutablePolygon.hpp"
@@ -1,8 +1,15 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <queue>
#include <cstddef>
#include <iterator>
#include <functional>
#include <cstdlib>
#include <random>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/MutablePriorityQueue.hpp"
// based on https://raw.githubusercontent.com/rollbear/prio_queue/master/self_test.cpp
@@ -1,5 +1,9 @@
#include <catch2/catch_all.hpp>
#include <set>
#include "libslic3r/Config.hpp"
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/PrintConfig.hpp"
using namespace Slic3r;
+7
View File
@@ -1,9 +1,16 @@
#include <array>
#include <catch2/catch_all.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Format/OBJ.hpp"
#include <boost/nowide/fstream.hpp>
#include <string>
#include "libslic3r/Point.hpp"
#include <cstddef>
#include "test_utils.hpp"
+4
View File
@@ -1,9 +1,13 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "test_utils.hpp"
#include <cstdlib>
#include <libslic3r/Optimize/BruteforceOptimizer.hpp>
#include <libslic3r/Optimize/NLoptOptimizer.hpp>
#include "libslic3r/Optimize/Optimizer.hpp"
#include "libslic3r/libslic3r.h"
void check_opt_result(double score, double ref, double abs_err, double rel_err)
{
@@ -1,4 +1,12 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_all.hpp>
#include <vector>
#include <cstddef>
#include <math.h>
#include "libslic3r/libslic3r.h"
#include <cmath>
#include <cstdint>
#include <limits>
#define SLIC3R_TEST_HARNESS
@@ -1,5 +1,6 @@
#include <catch2/catch_all.hpp>
#include <cstddef>
#include <numeric>
#include <stdexcept>
#include <thread>
@@ -9,6 +10,7 @@
#include <tbb/parallel_for.h>
#include <tbb/task_group.h>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/ParallelResolve.hpp"
using namespace Slic3r;
@@ -1,5 +1,12 @@
#include <catch2/catch_all.hpp>
#include <string>
#include <memory>
#include "libslic3r/Config.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include "libslic3r/PlaceholderParser.hpp"
#include "libslic3r/PrintConfig.hpp"
+4
View File
@@ -1,5 +1,9 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Point.hpp"
#include "libslic3r/Polygon.hpp"
+11
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include "test_utils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PrintConfig.hpp"
@@ -12,7 +17,13 @@
#include <algorithm>
#include <array>
#include "libslic3r/Config.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <cstddef>
#include "libslic3r/Geometry.hpp"
#include <miniz.h>
#include <string>
#include <utility>
#include <vector>
using namespace Slic3r;
@@ -1,10 +1,21 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/file_status.hpp>
#include <catch2/catch_all.hpp>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <cstddef>
#include <fstream>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/ParallelResolve.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleMesh.hpp"
@@ -16,6 +27,23 @@
#include <algorithm>
#include <iostream>
#include <initializer_list>
#include <vector>
#include <string>
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Preset.hpp"
#include <utility>
#include "libslic3r/Config.hpp"
#include <map>
#include "libslic3r/libslic3r.h"
#include "libslic3r/PublishSettings.hpp"
#include <set>
#include "libslic3r/TriangleSelector.hpp"
#include <iterator>
#include <miniz.h>
#ifndef _WIN32
#include <unistd.h> // geteuid
#endif
using namespace Slic3r;
@@ -5956,3 +5984,214 @@ TEST_CASE("A filament's variant index follows the extruder type and nozzle volum
// the filament defines no Bowden High Flow variant
CHECK(PresetBundle::get_filament_variant_index(filament, printer, 1, nvtHighFlow) == 0);
}
TEST_CASE("A user preset saved over by another instance while its directory loads is read again under the lock", "[Preset][Bundle][InstanceLock]")
{
ScopedTemporaryDir temp_dir;
ScopedDataDir data_dir_scope(temp_dir.path());
RenameTestCollection coll;
Preset &parent = add_inmemory_preset(coll, "Parent Process");
parent.config.option<ConfigOptionFloat>("layer_height", true)->value = 0.24;
parent.is_system = true;
// Fewer than one batch, so every file is read before the first one is committed.
constexpr int children = 8;
static_assert(children <= int(resolve_batch_size));
const fs::path dir = temp_dir.path() / PRESET_PRINT_NAME;
for (int i = 0; i < children; ++ i)
write_minimal_child(dir / ("Child " + std::to_string(i) + ".json"), "Child " + std::to_string(i), "Parent Process");
// The first commit stands in for another instance saving every other preset after
// they were read and before they are committed.
std::string first;
auto save_the_others = [&](Preset &preset) {
if (! first.empty())
return;
first = preset.name;
for (int i = 0; i < children; ++ i) {
const std::string name = "Child " + std::to_string(i);
if (name != first)
std::ofstream((dir / (name + ".json")).string())
<< R"({"type":"process","name":")" << name
<< R"(","from":"User","version":"1.0.0","inherits":"Parent Process","layer_height":"0.3"})";
}
};
PresetsConfigSubstitutions substitutions;
coll.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::Disable, save_the_others);
REQUIRE_FALSE(first.empty());
CHECK(coll.error_count() == 0);
for (int i = 0; i < children; ++ i) {
const std::string name = "Child " + std::to_string(i);
const Preset *child = coll.find_preset(name);
REQUIRE(child != nullptr);
CHECK_THAT(child->config.opt_float("layer_height"), Catch::Matchers::WithinAbs(name == first ? 0.24 : 0.3, 1e-9));
}
}
TEST_CASE("A user preset removed by another instance while its directory loads is not installed", "[Preset][Bundle][InstanceLock]")
{
ScopedTemporaryDir temp_dir;
ScopedDataDir data_dir_scope(temp_dir.path());
RenameTestCollection coll;
Preset &parent = add_inmemory_preset(coll, "Parent Process");
parent.is_system = true;
// Fewer than one batch, so every file is read before the first one is committed.
constexpr int children = 8;
static_assert(children <= int(resolve_batch_size));
const fs::path dir = temp_dir.path() / PRESET_PRINT_NAME;
for (int i = 0; i < children; ++ i)
write_minimal_child(dir / ("Child " + std::to_string(i) + ".json"), "Child " + std::to_string(i), "Parent Process");
std::string first;
auto remove_the_others = [&](Preset &preset) {
if (! first.empty())
return;
first = preset.name;
for (int i = 0; i < children; ++ i)
if (const std::string name = "Child " + std::to_string(i); name != first)
fs::remove(dir / (name + ".json"));
};
PresetsConfigSubstitutions substitutions;
coll.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::Disable, remove_the_others);
REQUIRE_FALSE(first.empty());
CHECK(coll.error_count() == 0);
CHECK(coll.size() == 3); // the default preset, the parent and the first child
CHECK(coll.find_preset(first) != nullptr);
}
TEST_CASE("Without the instance lock a user preset loads but its file is not written back", "[Preset][Bundle][InstanceLock]")
{
ScopedTemporaryDir temp_dir;
ScopedDataDir data_dir_scope(temp_dir.path());
// A directory where the lock file belongs, so the lock cannot be taken.
fs::create_directories(fs::path(user_presets_lock_path()));
PresetBundle bundle;
const fs::path file = temp_dir.path() / PRESET_FILAMENT_NAME / "My PLA @Test Printer.json";
write_preset_with_inherits(bundle.filaments.default_preset().config, file, "My PLA @Test Printer", std::string());
const std::string before = read_file(file);
PresetsConfigSubstitutions substitutions;
bundle.filaments.load_presets(temp_dir.path().string(), PRESET_FILAMENT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::EnableSilent);
const Preset *preset = bundle.filaments.find_preset("My PLA @Test Printer");
REQUIRE(preset != nullptr);
CHECK(preset->config.option<ConfigOptionStrings>("compatible_printers")->values == std::vector<std::string>{ "Test Printer" });
CHECK(read_file(file) == before);
}
#ifndef _WIN32
// File permissions stop reading only on POSIX.
TEST_CASE("A user preset that cannot be read under the instance lock is counted and removed", "[Preset][Bundle][InstanceLock]")
{
if (::geteuid() == 0)
SKIP("file permissions do not stop root");
ScopedTemporaryDir temp_dir;
ScopedDataDir data_dir_scope(temp_dir.path());
RenameTestCollection coll;
Preset &parent = add_inmemory_preset(coll, "Parent Process");
parent.is_system = true;
const fs::path file = temp_dir.path() / PRESET_PRINT_NAME / "Unreadable.json";
write_minimal_child(file, "Unreadable", "Parent Process");
fs::permissions(file, fs::no_perms);
PresetsConfigSubstitutions substitutions;
coll.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::Disable);
CHECK(coll.error_count() == 1);
CHECK(coll.find_preset("Unreadable") == nullptr);
CHECK_FALSE(fs::exists(file));
}
#endif
#ifndef _WIN32
// Creating a symlink needs no privilege only on POSIX.
TEST_CASE("A user preset that is a symlink to itself is counted and removed while the rest still load", "[Preset][Bundle][InstanceLock]")
{
ScopedTemporaryDir temp_dir;
ScopedDataDir data_dir_scope(temp_dir.path());
RenameTestCollection coll;
Preset &parent = add_inmemory_preset(coll, "Parent Process");
parent.is_system = true;
const fs::path dir = temp_dir.path() / PRESET_PRINT_NAME;
write_minimal_child(dir / "Good A.json", "Good A", "Parent Process");
write_minimal_child(dir / "Good B.json", "Good B", "Parent Process");
const fs::path loop = dir / "Loop.json";
fs::create_symlink(loop.filename(), loop);
PresetsConfigSubstitutions substitutions;
REQUIRE_NOTHROW(coll.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::Disable));
CHECK(coll.error_count() == 1);
CHECK(coll.find_preset("Good A") != nullptr);
CHECK(coll.find_preset("Good B") != nullptr);
CHECK(coll.find_preset("Loop") == nullptr);
boost::system::error_code ec;
CHECK(fs::symlink_status(loop, ec).type() == fs::file_not_found);
}
#endif
#ifndef _WIN32
// Creating a symlink needs no privilege only on POSIX.
TEST_CASE("A user preset symlinked to a missing target is counted but the link is kept", "[Preset][Bundle][InstanceLock]")
{
ScopedTemporaryDir temp_dir;
ScopedDataDir data_dir_scope(temp_dir.path());
RenameTestCollection coll;
const fs::path dir = temp_dir.path() / PRESET_PRINT_NAME;
const fs::path link = dir / "Linked.json";
fs::create_directories(dir);
fs::create_symlink(temp_dir.path() / "unmounted" / "Linked.json", link);
PresetsConfigSubstitutions substitutions;
coll.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::Disable);
CHECK(coll.error_count() == 1);
CHECK(coll.find_preset("Linked") == nullptr);
boost::system::error_code ec;
CHECK(fs::symlink_status(link, ec).type() == fs::symlink_file);
}
#endif
#ifndef _WIN32
// The read-only bit on a directory stops file creation only on POSIX.
TEST_CASE("A user filament whose derived compatible printer cannot be written back still loads with the failure counted", "[Preset][Bundle]")
{
if (::geteuid() == 0)
SKIP("a read-only directory does not stop root");
ScopedTemporaryDir temp_dir;
PresetBundle bundle;
const fs::path dir = temp_dir.path() / PRESET_FILAMENT_NAME;
const fs::path file = dir / "My PLA @Test Printer.json";
write_preset_with_inherits(bundle.filaments.default_preset().config, file, "My PLA @Test Printer", std::string());
const std::string before = read_file(file);
fs::permissions(file, fs::owner_read);
fs::permissions(dir, fs::owner_read | fs::owner_exe);
PresetsConfigSubstitutions substitutions;
bundle.filaments.load_presets(temp_dir.path().string(), PRESET_FILAMENT_NAME, substitutions,
ForwardCompatibilitySubstitutionRule::EnableSilent);
// Restored before any assertion, so a failure never leaves an unremovable directory behind.
fs::permissions(dir, fs::owner_all);
fs::permissions(file, fs::owner_read | fs::owner_write);
const Preset *preset = bundle.filaments.find_preset("My PLA @Test Printer");
REQUIRE(preset != nullptr);
CHECK(preset->config.option<ConfigOptionStrings>("compatible_printers")->values == std::vector<std::string>{ "Test Printer" });
CHECK(bundle.filaments.error_count() == 1);
CHECK(read_file(file) == before);
}
#endif
+6
View File
@@ -1,9 +1,15 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/Preset.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <algorithm>
#include "libslic3r/Config.hpp"
#include <vector>
#include <cstddef>
#include <string>
using namespace Slic3r;
+4
View File
@@ -16,10 +16,14 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/Preset.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Config.hpp"
#include <set>
#include <string>
using namespace Slic3r;
@@ -1,5 +1,8 @@
#include <catch2/catch_all.hpp>
#include <string>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/Preset.hpp"
using namespace Slic3r;
@@ -1,4 +1,11 @@
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <cstdlib>
#include <math.h>
#include <utility>
#include <vector>
#include "libslic3r/CAD/SketchConstraints.hpp"
#include "libslic3r/CAD/SketchEngine.hpp"
+7
View File
@@ -1,9 +1,16 @@
#include <TopAbs_ShapeEnum.hxx>
#include <TopoDS_Wire.hxx>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include "libslic3r/CAD/SketchEngine.hpp"
#include <cmath>
#include <algorithm>
#include <TopExp_Explorer.hxx>
#include <TopAbs.hxx>
#include <math.h>
#include <vector>
using namespace Slic3r;
+6
View File
@@ -1,10 +1,16 @@
#include <algorithm>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/CAD/SketchImport.hpp"
#include "libslic3r/Utils.hpp" // resources_dir
#include "test_utils.hpp" // ScopedTemporaryFile
#include <cstdlib>
#include <fstream>
#include "libslic3r/Point.hpp"
#include <string>
using namespace Slic3r;
+5
View File
@@ -1,4 +1,9 @@
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include "libslic3r/CAD/SketchEngine.hpp"
#include "libslic3r/Point.hpp"
#include <vector>
using Catch::Approx; // v3 scopes Approx into the Catch namespace; v2 had it at global scope
#include "libslic3r/CAD/SketchInference.hpp"
+8
View File
@@ -9,11 +9,19 @@
// So these cases assert the loop, not the coordinates. That is the invariant every sketch
// operation has to preserve and the only one that predicts whether the GUI can build a solid
// out of the result.
#include <TopoDS_Face.hxx>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include "libslic3r/CAD/SketchEngine.hpp"
#include <BRepGProp.hxx>
#include <GProp_GProps.hxx>
#include <cmath>
#include "libslic3r/Point.hpp"
#include <vector>
#include <math.h>
#include <cstddef>
using namespace Slic3r;
using Catch::Matchers::WithinAbs;
@@ -1,4 +1,9 @@
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include "libslic3r/Point.hpp"
#include <vector>
#include <cstddef>
using Catch::Approx; // v3 scopes Approx into the Catch namespace; v2 had it at global scope
#include "libslic3r/CAD/SketchSolver.hpp"
+4
View File
@@ -1,7 +1,11 @@
#include <catch2/catch_all.hpp>
#include <boost/nowide/fstream.hpp>
#include <string>
#include <ios>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/Model.hpp"
#include "libslic3r/Format/STEP.hpp"
#include "test_utils.hpp"
+3
View File
@@ -1,5 +1,8 @@
#include <catch2/catch_all.hpp>
#include <string>
#include "libslic3r/Point.hpp"
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Model.hpp"
#include "libslic3r/Format/STL.hpp"
@@ -1,6 +1,29 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <memory>
#include <vector>
#include <cstdint>
#include <cstddef>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_test_macros.hpp>
#include <ios>
#include <iterator>
#include <utility>
#include "libslic3r/Point.hpp"
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <map>
#include <math.h>
#include "libslic3r/TextureBake/TextureBakeDisplace.hpp"
#include "libslic3r/TextureBake/TextureBakeIndex.hpp"
#include <tuple>
#include <Eigen/Geometry>
#include "libslic3r/libslic3r.h"
#include <catch2/catch_message.hpp>
#include <catch2/catch_all.hpp>
#include <algorithm>
+3
View File
@@ -1,10 +1,13 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "libslic3r/Time.hpp"
#include <ctime>
#include <sstream>
#include <iomanip>
#include <locale>
#include <string>
using namespace Slic3r;
@@ -1,5 +1,9 @@
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include "libslic3r/FilamentGroupUtils.hpp"
#include "libslic3r/MultiNozzleUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
@@ -11,7 +15,11 @@
#include "test_utils.hpp"
#include <algorithm>
#include "libslic3r/Config.hpp"
#include "libslic3r/GCode/ToolOrderUtils.hpp"
#include "libslic3r/PrintBase.hpp"
#include <map>
#include <memory>
#include <set>
#include <unordered_map>
#include <vector>
@@ -510,6 +518,22 @@ TEST_CASE("Print config-index resolvers pick per-filament Hybrid slots", "[Print
}
}
TEST_CASE("Regrouping or rewriting the filament maps changes the config-index generation", "[Print][H2C]")
{
Model model;
model.add_object("cube", "", make_cube(20, 20, 20))->add_instance();
Print print;
print.apply(model, DynamicPrintConfig::full_print_config());
size_t generation = print.config_index_generation();
print.set_nozzle_group_result(nullptr);
REQUIRE(print.config_index_generation() != generation);
generation = print.config_index_generation();
print.update_filament_maps_to_config({1}, {(int) nvtStandard}, {0});
REQUIRE(print.config_index_generation() != generation);
}
TEST_CASE("Re-applying an unchanged config after slicing keeps the result valid", "[Print][H2C]")
{
// apply() rebuilds m_config.filament_map_2 to the real per-filament slot map, while the
@@ -1,9 +1,16 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/TriangleSelector.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <algorithm>
#include "libslic3r/libslic3r.h"
#include <vector>
#include <cstddef>
#include <string>
using namespace Slic3r;
@@ -0,0 +1,50 @@
#include <catch2/catch_all.hpp>
#include <tbb/global_control.h>
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"
using namespace Slic3r;
// The slab slicer collects each slab's intersection lines from a parallel loop over the facets.
// Its loops, and therefore the projected polygons, are derived from the order of those lines, so
// the order has to be canonical or the same mesh projects to different polygons run to run.
// The single-threaded projection is the reference; every multi-threaded run must reproduce it
// exactly, vertex order included.
TEST_CASE("Slab slicing projects the same polygons whatever the thread schedule", "[TriangleMeshSlicer]")
{
// A dense sphere, tilted so no facet is axis aligned: thousands of upward and downward
// facing facets spread over every slab.
indexed_triangle_set mesh = its_make_sphere(10., 0.05);
Transform3d trafo = Transform3d::Identity();
trafo.rotate(Eigen::AngleAxisd(0.37, Vec3d(0.3, 0.5, 1.).normalized()));
trafo.translate(Vec3d(1., 2., 0.));
std::vector<float> zs;
for (float z = -9.7f; z < 9.7f; z += 0.2f)
zs.emplace_back(z);
auto project = [&mesh, &trafo, &zs]() {
std::vector<Polygons> top, bottom;
slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, nullptr, []{});
return std::make_pair(std::move(top), std::move(bottom));
};
std::pair<std::vector<Polygons>, std::vector<Polygons>> reference;
{
tbb::global_control single_thread(tbb::global_control::max_allowed_parallelism, 1);
reference = project();
}
REQUIRE(reference.first.size() == zs.size());
REQUIRE(std::any_of(reference.first.begin(), reference.first.end(), [](const Polygons &p) { return !p.empty(); }));
REQUIRE(std::any_of(reference.second.begin(), reference.second.end(), [](const Polygons &p) { return !p.empty(); }));
for (int run = 0; run < 3; ++run) {
DYNAMIC_SECTION("multi-threaded run " << run)
{
auto parallel = project();
CHECK(parallel.first == reference.first);
CHECK(parallel.second == reference.second);
}
}
}
+122
View File
@@ -1,5 +1,14 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/file_status.hpp>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/Utils.hpp"
#include "test_utils.hpp"
@@ -8,8 +17,14 @@
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <fstream>
#include <ios>
#include <iterator>
#include <string>
#include <thread>
#include <system_error>
#include <utility>
#ifndef _WIN32
#include <unistd.h> // getuid
@@ -62,6 +77,113 @@ TEST_CASE("per-user temp root is unchanged on Windows, isolated elsewhere", "[ut
#endif
}
TEST_CASE("write_file_atomically replaces the target and leaves no temporary file", "[utils]") {
ScopedTemporaryDir dir;
const boost::filesystem::path target = dir.path() / "preset.json";
REQUIRE_FALSE(write_file_atomically(target.string(), "first"));
REQUIRE_FALSE(write_file_atomically(target.string(), "second"));
std::string content;
load_string_file(target, content);
REQUIRE(content == "second");
size_t entries = 0;
for (auto &entry : boost::filesystem::directory_iterator(dir.path())) {
(void) entry;
++entries;
}
REQUIRE(entries == 1);
}
TEST_CASE("write_file_atomically reports a missing directory and writes nothing", "[utils]") {
ScopedTemporaryDir dir;
const boost::filesystem::path target = dir.path() / "missing" / "preset.json";
const std::error_code ec = write_file_atomically(target.string(), "x");
REQUIRE(ec == std::errc::no_such_file_or_directory);
REQUIRE_FALSE(boost::filesystem::exists(target));
}
#ifndef _WIN32
// The read-only bit on a directory stops file creation only on POSIX.
TEST_CASE("write_file_atomically writes in place when no temporary can be created beside an existing target", "[utils]") {
if (::geteuid() == 0)
SKIP("a read-only directory does not stop root");
ScopedTemporaryDir dir;
const boost::filesystem::path target = dir.path() / "preset.json";
REQUIRE_FALSE(write_file_atomically(target.string(), "first"));
boost::filesystem::permissions(dir.path(), boost::filesystem::owner_read | boost::filesystem::owner_exe);
const std::error_code replaced = write_file_atomically(target.string(), "second");
const std::error_code created = write_file_atomically((dir.path() / "new.json").string(), "x");
// Restored before any assertion, so a failure never leaves an unremovable directory behind.
boost::filesystem::permissions(dir.path(), boost::filesystem::owner_all);
REQUIRE_FALSE(replaced);
REQUIRE(created == std::errc::permission_denied);
std::string content;
load_string_file(target, content);
REQUIRE(content == "second");
}
#endif
TEST_CASE("write_file_atomically keeps bytes intact in binary mode", "[utils]") {
ScopedTemporaryDir dir;
const boost::filesystem::path target = dir.path() / "blob.bin";
const std::string bytes("a\r\nb\0c", 6);
REQUIRE_FALSE(write_file_atomically(target.string(), bytes, /*binary=*/true));
REQUIRE(boost::filesystem::file_size(target) == bytes.size());
}
#ifndef _WIN32
TEST_CASE("write_file_atomically writes through a symlink and keeps the target's permissions", "[utils]") {
ScopedTemporaryDir dir;
const boost::filesystem::path real = dir.path() / "real.json";
const boost::filesystem::path link = dir.path() / "link.json";
REQUIRE_FALSE(write_file_atomically(real.string(), "first"));
boost::filesystem::permissions(real, boost::filesystem::owner_read | boost::filesystem::owner_write);
boost::filesystem::create_symlink(real, link);
REQUIRE_FALSE(write_file_atomically(link.string(), "second"));
REQUIRE(boost::filesystem::is_symlink(boost::filesystem::symlink_status(link)));
std::string content;
load_string_file(real, content);
REQUIRE(content == "second");
REQUIRE_FALSE(write_file_atomically(real.string(), "third"));
const auto perms = boost::filesystem::status(real).permissions() & boost::filesystem::all_all;
REQUIRE(perms == (boost::filesystem::owner_read | boost::filesystem::owner_write));
}
#endif
TEST_CASE("write_file_atomically survives two threads writing one target", "[utils]") {
ScopedTemporaryDir dir;
const boost::filesystem::path target = dir.path() / "shared.json";
const std::string a(20000, 'a'), b(20000, 'b');
std::thread other([&] {
for (int i = 0; i < 50; ++i)
write_file_atomically(target.string(), a);
});
for (int i = 0; i < 50; ++i)
write_file_atomically(target.string(), b);
other.join();
std::string content;
load_string_file(target, content);
const bool whole = content == a || content == b;
REQUIRE(whole);
// No temporary may be left; a scanner on Windows may briefly hold the old
// file under another name, so only the temporaries are counted.
size_t temporaries = 0;
for (auto &entry : boost::filesystem::directory_iterator(dir.path()))
if (entry.path().extension() == ".tmp")
++temporaries;
REQUIRE(temporaries == 0);
}
TEST_CASE("copy_file reports the OS error when the destination cannot be written", "[utils]") {
ScopedTemporaryFile source(".txt");
{
+15 -18
View File
@@ -1,3 +1,5 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <catch2/catch_all.hpp>
#include <boost/filesystem.hpp>
@@ -5,19 +7,32 @@
#include <cereal/archives/binary.hpp>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <functional>
#include <ios>
#include <iterator>
#include "libslic3r/Semver.hpp"
#include "libslic3r/Config.hpp"
#include <memory>
#include <numeric>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <stdexcept>
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include <tbb/task_group.h>
#include <vector>
#include <utility>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_message.hpp>
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/PresetCacheFormat.hpp"
#include "libslic3r/Preset.hpp"
@@ -1804,24 +1819,6 @@ TEST_CASE("a header claiming more body than the file holds is rejected", "[Vendo
REQUIRE_FALSE(bundle.load_vendor_cache(cache, "Bounded", Semver(1, 0, 0)));
}
TEST_CASE("a failed write leaves the previous cache in place", "[VendorCache]")
{
TempDir tmp;
const std::string cache = (tmp.path / "Durable.opc").string();
REQUIRE(save_one_vendor(cache, one_vendor("Durable"), "Durable", "1.0.0"));
const std::string before = slurp(cache);
// A directory where the temp file wants to go: the write cannot complete,
// and must not have destroyed what was already there to find that out.
const fs::path blocker = fs::path(cache + "." + std::to_string(get_current_pid()) + ".tmp");
fs::create_directories(blocker);
REQUIRE_FALSE(save_one_vendor(cache, one_vendor("Durable"), "Durable", "2.0.0"));
CHECK(slurp(cache) == before);
fs::remove_all(blocker);
}
TEST_CASE("a cache written by another build's option ordering still loads", "[VendorCache]")
{
// The regression the fingerprint used to prevent by refusing the file
+11
View File
@@ -1,6 +1,14 @@
#include <algorithm>
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include "test_utils.hpp"
#include "libslic3r/Geometry/Voronoi.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Line.hpp"
#include <cmath>
#include <cstddef>
#include "libslic3r/BoundingBox.hpp"
#include <libslic3r/Polygon.hpp>
#include <libslic3r/Polyline.hpp>
#include <libslic3r/EdgeGrid.hpp>
@@ -9,8 +17,11 @@
#include <libslic3r/Geometry/VoronoiOffset.hpp>
#include <libslic3r/Geometry/VoronoiVisualUtils.hpp>
#include "libslic3r/libslic3r.h"
#include <math.h>
#include <numeric>
#include <random>
#include <vector>
// #define VORONOI_DEBUG_OUT
+10
View File
@@ -1,5 +1,10 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/generators/catch_generators.hpp>
#include "libslic3r/GCode/WipePathHelpers.hpp"
#include "libslic3r/AABBTreeLines.hpp"
#include "libslic3r/Polyline.hpp"
@@ -9,7 +14,12 @@
#include <algorithm>
#include <cmath>
#include "libslic3r/ExtrusionEntity.hpp"
#include <cstddef>
#include <limits>
#include <vector>
#include <utility>
#include <optional>
using namespace Slic3r;
using Slic3r::AABBTreeLines::LinesDistancer;
+10
View File
@@ -1,7 +1,17 @@
#include <catch2/catch_all.hpp>
#include <cmath>
#include <vector>
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Point.hpp"
#include <math.h>
#include <utility>
#include "libslic3r/libslic3r.h"
#include <cstddef>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/GCode/WipeTower.hpp"

Some files were not shown because too many files have changed in this diff Show More