Merge prusa 2.6.1

This commit is contained in:
QIDI TECH
2023-09-16 16:26:29 +08:00
parent 1338e60f8b
commit 963e22db99
203 changed files with 25254 additions and 6453 deletions

View File

@@ -27,7 +27,7 @@ endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
add_subdirectory(libnest2d)
add_subdirectory(arrange)
add_subdirectory(libslic3r)
add_subdirectory(slic3rutils)
add_subdirectory(fff_print)

View File

@@ -0,0 +1,17 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp
test_arrange.cpp
test_arrange_integration.cpp
../data/qidiparts.cpp
)
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
if (WIN32)
qidislicer_copy_dlls(${_TEST_NAME}_tests)
endif()
set(_catch_args "exclude:[NotWorking] exclude:[Slow]")
list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args})

View File

@@ -0,0 +1 @@
#include <catch_main.hpp>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

312
tests/data/default_fff.ini Normal file
View File

@@ -0,0 +1,312 @@
autoemit_temperature_commands = 1
avoid_crossing_curled_overhangs = 0
avoid_crossing_perimeters = 0
avoid_crossing_perimeters_max_detour = 0
bed_custom_model =
bed_custom_texture =
bed_shape = 0x0,250x0,250x210,0x210
bed_temperature = 110
before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\n\n
between_objects_gcode =
bottom_fill_pattern = monotonic
bottom_solid_layers = 4
bottom_solid_min_thickness = 0.5
bridge_acceleration = 1000
bridge_angle = 0
bridge_fan_speed = 25
bridge_flow_ratio = 0.95
bridge_speed = 25
brim_separation = 0.1
brim_type = outer_only
brim_width = 0
color_change_gcode = M600\nG1 E0.4 F1500 ; prime after color change
colorprint_heights =
compatible_printers_condition_cummulative = "printer_notes=~/.*PRINTER_VENDOR_QIDI3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4";"nozzle_diameter[0]!=0.8 and printer_model!=\"MINI\" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_QIDI3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)"
complete_objects = 0
cooling = 1
cooling_tube_length = 5
cooling_tube_retraction = 91.5
default_acceleration = 1000
default_filament_profile = "QIDIment PLA"
default_print_profile = 0.15mm QUALITY @MK3
deretract_speed = 0
disable_fan_first_layers = 4
dont_support_bridges = 0
draft_shield = disabled
duplicate_distance = 6
elefant_foot_compensation = 0.2
enable_dynamic_fan_speeds = 0
enable_dynamic_overhang_speeds = 1
end_filament_gcode = "; Filament-specific end gcode"
end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM84 ; disable motors\n; max_layer_z = [max_layer_z]
external_perimeter_acceleration = 0
external_perimeter_extrusion_width = 0.45
external_perimeter_speed = 25
external_perimeters_first = 0
extra_loading_move = -2
extra_perimeters = 0
extra_perimeters_on_overhangs = 0
extruder_clearance_height = 20
extruder_clearance_radius = 45
extruder_colour = ""
extruder_offset = 0x0
extrusion_axis = E
extrusion_multiplier = 1
extrusion_width = 0.45
fan_always_on = 0
fan_below_layer_time = 30
filament_colour = #FFF2EC
filament_cooling_final_speed = 3.4
filament_cooling_initial_speed = 2.2
filament_cooling_moves = 4
filament_cost = 27.82
filament_density = 1.04
filament_deretract_speed = nil
filament_diameter = 1.75
filament_load_time = 0
filament_loading_speed = 28
filament_loading_speed_start = 3
filament_max_volumetric_speed = 11
filament_minimal_purge_on_wipe_tower = 15
filament_notes = ""
filament_ramming_parameters = "120 100 5.70968 6.03226 7 8.25806 9 9.19355 9.3871 9.77419 10.129 10.3226 10.4516 10.5161| 0.05 5.69677 0.45 6.15484 0.95 8.76774 1.45 9.20323 1.95 9.95806 2.45 10.3871 2.95 10.5677 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6"
filament_retract_before_travel = nil
filament_retract_before_wipe = nil
filament_retract_layer_change = nil
filament_retract_length = nil
filament_retract_lift = nil
filament_retract_lift_above = nil
filament_retract_lift_below = nil
filament_retract_restart_extra = nil
filament_retract_speed = nil
filament_settings_id = "Generic ABS"
filament_soluble = 0
filament_spool_weight = 0
filament_toolchange_delay = 0
filament_type = ABS
filament_unload_time = 0
filament_unloading_speed = 90
filament_unloading_speed_start = 100
filament_vendor = Generic
filament_wipe = nil
fill_angle = 45
fill_density = 15%
fill_pattern = gyroid
first_layer_acceleration = 800
first_layer_acceleration_over_raft = 0
first_layer_bed_temperature = 100
first_layer_extrusion_width = 0.42
first_layer_height = 0.2
first_layer_speed = 20
first_layer_speed_over_raft = 30
first_layer_temperature = 255
full_fan_speed_layer = 0
fuzzy_skin = none
fuzzy_skin_point_dist = 0.8
fuzzy_skin_thickness = 0.3
gap_fill_enabled = 1
gap_fill_speed = 40
gcode_comments = 0
gcode_flavor = marlin
gcode_label_objects = 1
gcode_resolution = 0.0125
gcode_substitutions =
high_current_on_filament_swap = 0
host_type = qidilink
idle_temperature = nil
infill_acceleration = 1000
infill_anchor = 2.5
infill_anchor_max = 12
infill_every_layers = 1
infill_extruder = 1
infill_extrusion_width = 0.45
infill_first = 0
infill_overlap = 10%
infill_speed = 80
interface_shells = 0
ironing = 0
ironing_flowrate = 15%
ironing_spacing = 0.1
ironing_speed = 15
ironing_type = top
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
layer_height = 0.2
machine_limits_usage = emit_to_gcode
machine_max_acceleration_e = 5000,5000
machine_max_acceleration_extruding = 1250,1250
machine_max_acceleration_retracting = 1250,1250
machine_max_acceleration_travel = 1500,1250
machine_max_acceleration_x = 1000,960
machine_max_acceleration_y = 1000,960
machine_max_acceleration_z = 200,200
machine_max_feedrate_e = 120,120
machine_max_feedrate_x = 200,100
machine_max_feedrate_y = 200,100
machine_max_feedrate_z = 12,12
machine_max_jerk_e = 4.5,4.5
machine_max_jerk_x = 8,8
machine_max_jerk_y = 8,8
machine_max_jerk_z = 0.4,0.4
machine_min_extruding_rate = 0,0
machine_min_travel_rate = 0,0
max_fan_speed = 15
max_layer_height = 0.25
max_print_height = 210
max_print_speed = 200
max_volumetric_extrusion_rate_slope_negative = 0
max_volumetric_extrusion_rate_slope_positive = 0
max_volumetric_speed = 0
min_bead_width = 85%
min_fan_speed = 15
min_feature_size = 25%
min_layer_height = 0.07
min_print_speed = 15
min_skirt_length = 4
mmu_segmented_region_max_width = 0
notes =
nozzle_diameter = 0.4
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode
overhang_fan_speed_0 = 0
overhang_fan_speed_1 = 0
overhang_fan_speed_2 = 0
overhang_fan_speed_3 = 0
overhang_speed_0 = 15
overhang_speed_1 = 15
overhang_speed_2 = 20
overhang_speed_3 = 25
overhangs = 1
parking_pos_retraction = 92
pause_print_gcode = M601
perimeter_acceleration = 800
perimeter_extruder = 1
perimeter_extrusion_width = 0.45
perimeter_generator = arachne
perimeter_speed = 45
perimeters = 2
physical_printer_settings_id =
post_process =
print_settings_id = 0.20mm QUALITY @MK3
printer_model = MK3
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
printer_settings_id = Original QIDI i3 MK3
printer_technology = FFF
printer_variant = 0.4
printer_vendor =
raft_contact_distance = 0.2
raft_expansion = 1.5
raft_first_layer_density = 90%
raft_first_layer_expansion = 3
raft_layers = 0
remaining_times = 1
resolution = 0
retract_before_travel = 1
retract_before_wipe = 0%
retract_layer_change = 1
retract_length = 0.8
retract_length_toolchange = 4
retract_lift = 0.4
retract_lift_above = 0
retract_lift_below = 209
retract_restart_extra = 0
retract_restart_extra_toolchange = 0
retract_speed = 35
seam_position = aligned
silent_mode = 1
single_extruder_multi_material = 0
single_extruder_multi_material_priming = 0
skirt_distance = 2
skirt_height = 3
skirts = 1
slice_closing_radius = 0.049
slicing_mode = regular
slowdown_below_layer_time = 20
small_perimeter_speed = 25
solid_infill_acceleration = 0
solid_infill_below_area = 0
solid_infill_every_layers = 0
solid_infill_extruder = 1
solid_infill_extrusion_width = 0.45
solid_infill_speed = 80
spiral_vase = 0
staggered_inner_seams = 0
standby_temperature_delta = -5
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0"
start_gcode = M862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.12.2 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n{if filament_settings_id[initial_tool]=~/.*QIDIment PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif}
support_material = 0
support_material_angle = 0
support_material_auto = 1
support_material_bottom_contact_distance = 0
support_material_bottom_interface_layers = 0
support_material_buildplate_only = 0
support_material_closing_radius = 2
support_material_contact_distance = 0.2
support_material_enforce_layers = 0
support_material_extruder = 0
support_material_extrusion_width = 0.35
support_material_interface_contact_loops = 0
support_material_interface_extruder = 0
support_material_interface_layers = 2
support_material_interface_pattern = rectilinear
support_material_interface_spacing = 0.2
support_material_interface_speed = 80%
support_material_pattern = rectilinear
support_material_spacing = 2
support_material_speed = 50
support_material_style = grid
support_material_synchronize_layers = 0
support_material_threshold = 50
support_material_with_sheath = 0
support_material_xy_spacing = 60%
support_tree_angle = 40
support_tree_angle_slow = 25
support_tree_branch_diameter = 2
support_tree_branch_diameter_angle = 5
support_tree_branch_diameter_double_wall = 3
support_tree_branch_distance = 1
support_tree_tip_diameter = 0.8
support_tree_top_rate = 15%
temperature = 255
template_custom_gcode =
thick_bridges = 0
thin_walls = 0
threads = 24
thumbnails = 160x120
thumbnails_format = PNG
toolchange_gcode =
top_fill_pattern = monotoniclines
top_infill_extrusion_width = 0.4
top_solid_infill_acceleration = 0
top_solid_infill_speed = 40
top_solid_layers = 5
top_solid_min_thickness = 0.7
travel_acceleration = 0
travel_speed = 180
travel_speed_z = 12
use_firmware_retraction = 0
use_relative_e_distances = 1
use_volumetric_e = 0
variable_layer_height = 1
wall_distribution_count = 1
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 100%
wipe = 1
wipe_into_infill = 0
wipe_into_objects = 0
wipe_tower = 1
wipe_tower_bridging = 10
wipe_tower_brim_width = 2
wipe_tower_cone_angle = 0
wipe_tower_extra_spacing = 100%
wipe_tower_no_sparse_layers = 0
wipe_tower_rotation_angle = 0
wipe_tower_width = 60
wipe_tower_x = 170
wipe_tower_y = 125
wiping_volumes_extruders = 70,70
wiping_volumes_matrix = 0
xy_size_compensation = 0
z_offset = 0

5981
tests/data/qidiparts.cpp Normal file

File diff suppressed because it is too large Load Diff

14
tests/data/qidiparts.hpp Normal file
View File

@@ -0,0 +1,14 @@
#ifndef QIDIPARTS_H
#define QIDIPARTS_H
#include <vector>
#include <libslic3r/ExPolygon.hpp>
using TestData = std::vector<Slic3r::Polygon>;
using TestDataEx = std::vector<Slic3r::ExPolygons>;
extern const TestData QIDI_PART_POLYGONS;
extern const TestData QIDI_STEGOSAUR_POLYGONS;
extern const TestDataEx QIDI_PART_POLYGONS_EX;
#endif // QIDIPARTS_H

View File

@@ -228,7 +228,7 @@ void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r
object->add_volume(std::move(t));
object->add_instance();
}
arrange_objects(model, InfiniteBed{}, ArrangeParams{ scaled(min_object_distance(config))});
arrange_objects(model, arr2::to_arrange_bed(get_bed_shape(config)), arr2::ArrangeSettings{}.set_distance_from_objects(min_object_distance(config)));
model.center_instances_around_point({100, 100});
for (ModelObject *mo : model.objects) {
mo->ensure_on_bed();

View File

@@ -42,8 +42,12 @@ SCENARIO("Model construction", "[Model]") {
}
}
model_object->add_instance();
arrange_objects(model, InfiniteBed{scaled(Vec2d(100, 100))}, ArrangeParams{scaled(min_object_distance(config))});
model_object->ensure_on_bed();
arrange_objects(model,
arr2::to_arrange_bed(get_bed_shape(config)),
arr2::ArrangeSettings{}.set_distance_from_objects(
min_object_distance(config)));
model_object->ensure_on_bed();
print.auto_assign_extruders(model_object);
THEN("Print works?") {
print.set_status_silent();

View File

@@ -1,11 +0,0 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp)
# mold linker for successful linking needs also to link TBB library and link it before libslic3r.
target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libnest2d )
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
set(_catch_args "exclude:[NotWorking]")
list(APPEND _catch_args "${CATCH_EXTRA_ARGS}")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${_catch_args})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +0,0 @@
#ifndef PRINTER_PARTS_H
#define PRINTER_PARTS_H
#include <vector>
#include <libnest2d/libnest2d.hpp>
using TestData = std::vector<libnest2d::PathImpl>;
using TestDataEx = std::vector<libnest2d::PolygonImpl>;
extern const TestData PRINTER_PART_POLYGONS;
extern const TestData STEGOSAUR_POLYGONS;
extern const TestDataEx PRINTER_PART_POLYGONS_EX;
#endif // PRINTER_PARTS_H

View File

@@ -31,13 +31,15 @@ add_executable(${_TEST_NAME}_tests
test_png_io.cpp
test_surface_mesh.cpp
test_timeutils.cpp
test_quadric_edge_collapse.cpp
test_quadric_edge_collapse.cpp
test_triangulation.cpp
test_emboss.cpp
test_indexed_triangle_set.cpp
test_astar.cpp
test_jump_point_search.cpp
../libnest2d/printer_parts.cpp
test_anyptr.cpp
test_jump_point_search.cpp
../data/qidiparts.cpp
../data/qidiparts.hpp
)
if (TARGET OpenVDB::openvdb)

View File

@@ -0,0 +1,198 @@
#include <catch2/catch.hpp>
#include <libslic3r/libslic3r.h>
#include <libslic3r/AnyPtr.hpp>
#include <test_utils.hpp>
class Foo
{
public:
virtual ~Foo() = default;
virtual void set_foo(int) = 0;
virtual int get_foo() const = 0;
};
class Bar: public Foo
{
int m_i = 0;
public:
virtual void set_foo(int i) { m_i = i; }
virtual int get_foo() const { return m_i; };
};
class BarPlus: public Foo {
int m_i = 0;
public:
virtual void set_foo(int i) { m_i = i + 1; }
virtual int get_foo() const { return m_i; };
};
TEST_CASE("Testing AnyPtr", "[anyptr]") {
using Slic3r::AnyPtr;
SECTION("Construction with various valid arguments using operator=")
{
auto args = std::make_tuple(nullptr,
AnyPtr<Foo>{nullptr},
AnyPtr{static_cast<Foo *>(nullptr)},
AnyPtr{static_cast<Bar *>(nullptr)},
AnyPtr{static_cast<BarPlus *>(nullptr)},
AnyPtr<Foo>{},
AnyPtr<Bar>{},
AnyPtr<BarPlus>{},
static_cast<Foo *>(nullptr),
static_cast<Bar *>(nullptr),
static_cast<BarPlus *>(nullptr));
auto check_ptr = [](auto &ptr) {
REQUIRE(!ptr);
REQUIRE(!ptr.is_owned());
auto shp = ptr.get_shared_cpy();
REQUIRE(!shp);
};
SECTION("operator =") {
Slic3r::for_each_in_tuple([&check_ptr](auto &arg){
AnyPtr<const Foo> ptr = std::move(arg);
check_ptr(ptr);
}, args);
}
SECTION("move construction")
{
Slic3r::for_each_in_tuple([&check_ptr](auto &arg){
AnyPtr<const Foo> ptr{std::move(arg)};
check_ptr(ptr);
}, args);
}
}
GIVEN("A polymorphic base class type Foo") {
WHEN("Creating a subclass on the stack") {
Bar bar;
auto val = random_value(-100, 100);
bar.set_foo(val);
THEN("Storing a raw pointer in an AnyPtr<Foo> should be valid "
"until the object is not destroyed")
{
AnyPtr<Foo> ptr = &bar;
auto val2 = random_value(-100, 100);
ptr->set_foo(val2);
REQUIRE(ptr->get_foo() == val2);
}
THEN("Storing a raw pointer in an AnyPtr<const Foo> should be "
"valid until the object is not destroyed")
{
AnyPtr<const Foo> ptr{&bar};
REQUIRE(ptr->get_foo() == val);
}
}
}
GIVEN("An empty AnyPtr of type Foo")
{
AnyPtr<Foo> ptr;
WHEN("Re-assigning a new unique_ptr of object of type Bar to ptr")
{
auto bar = std::make_unique<Bar>();
auto val = random_value(-100, 100);
bar->set_foo(val);
ptr = std::move(bar);
THEN("the ptr should contain the new object and should own it")
{
REQUIRE(ptr->get_foo() == val);
REQUIRE(ptr.is_owned());
}
}
WHEN("Re-assigning a new unique_ptr of object of type BarPlus to ptr")
{
auto barplus = std::make_unique<BarPlus>();
auto val = random_value(-100, 100);
barplus->set_foo(val);
ptr = std::move(barplus);
THEN("the ptr should contain the new object and should own it")
{
REQUIRE(ptr->get_foo() == val + 1);
REQUIRE(ptr.is_owned());
}
THEN("copying the stored object into a shared_ptr should be invalid")
{
std::shared_ptr<Foo> shptr = ptr.get_shared_cpy();
REQUIRE(!shptr);
}
THEN("copying the stored object into a shared_ptr after calling "
"convert_unique_to_shared should be valid")
{
ptr.convert_unique_to_shared();
std::shared_ptr<Foo> shptr = ptr.get_shared_cpy();
REQUIRE(shptr);
REQUIRE(shptr->get_foo() == val + 1);
}
}
}
GIVEN("A vector of AnyPtr<Foo> pointer to random Bar or BarPlus objects")
{
std::vector<AnyPtr<Foo>> ptrs;
auto N = random_value(size_t(1), size_t(10));
INFO("N = " << N);
std::generate_n(std::back_inserter(ptrs), N, []{
auto v = random_value(0, 1);
std::unique_ptr<Foo> ret;
if (v)
ret = std::make_unique<Bar>();
else
ret = std::make_unique<BarPlus>();
return ret;
});
WHEN("moving the whole array into a vector of AnyPtr<const Foo>")
{
THEN("the move should be valid")
{
std::vector<AnyPtr<const Foo>> constptrs;
std::vector<int> vals;
std::transform(ptrs.begin(), ptrs.end(),
std::back_inserter(vals),
[](auto &p) { return p->get_foo(); });
std::move(ptrs.begin(), ptrs.end(), std::back_inserter(constptrs));
REQUIRE(constptrs.size() == N);
REQUIRE(ptrs.size() == N);
REQUIRE(std::all_of(ptrs.begin(), ptrs.end(), [](auto &p) { return !p; }));
for (size_t i = 0; i < N; ++i) {
REQUIRE(vals[i] == constptrs[i]->get_foo());
}
}
}
}
}

View File

@@ -15,7 +15,7 @@
//#include "libnest2d/tools/benchmark.h"
#include "libslic3r/SVG.hpp"
#include "../libnest2d/printer_parts.hpp"
#include "../data/qidiparts.hpp"
#include <unordered_set>
@@ -683,15 +683,15 @@ struct Pair
template<> struct std::hash<Pair> {
size_t operator()(const Pair &c) const
{
return c.first * PRINTER_PART_POLYGONS.size() + c.second;
return c.first * QIDI_PART_POLYGONS.size() + c.second;
}
};
TEST_CASE("Convex polygon intersection test qidi polygons", "[Geometry][Rotcalip]") {
// Overlap of the same polygon should always be an intersection
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
Polygon P = PRINTER_PART_POLYGONS[i];
for (size_t i = 0; i < QIDI_PART_POLYGONS.size(); ++i) {
Polygon P = QIDI_PART_POLYGONS[i];
P = Geometry::convex_hull(P.points);
bool res = Geometry::convex_polygons_intersect(P, P);
if (!res) {
@@ -703,8 +703,8 @@ TEST_CASE("Convex polygon intersection test qidi polygons", "[Geometry][Rotcalip
}
std::unordered_set<Pair> combos;
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
for (size_t j = 0; j < PRINTER_PART_POLYGONS.size(); ++j) {
for (size_t i = 0; i < QIDI_PART_POLYGONS.size(); ++i) {
for (size_t j = 0; j < QIDI_PART_POLYGONS.size(); ++j) {
if (i != j) {
size_t a = std::min(i, j), b = std::max(i, j);
combos.insert(Pair{a, b});
@@ -714,7 +714,7 @@ TEST_CASE("Convex polygon intersection test qidi polygons", "[Geometry][Rotcalip
// All disjoint
for (const auto &combo : combos) {
Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second];
Polygon A = QIDI_PART_POLYGONS[combo.first], B = QIDI_PART_POLYGONS[combo.second];
A = Geometry::convex_hull(A.points);
B = Geometry::convex_hull(B.points);
@@ -741,7 +741,7 @@ TEST_CASE("Convex polygon intersection test qidi polygons", "[Geometry][Rotcalip
// All intersecting
for (const auto &combo : combos) {
Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second];
Polygon A = QIDI_PART_POLYGONS[combo.first], B = QIDI_PART_POLYGONS[combo.second];
A = Geometry::convex_hull(A.points);
B = Geometry::convex_hull(B.points);

View File

@@ -1,5 +1,3 @@
#define NOMINMAX
#include <catch2/catch.hpp>
#include <test_utils.hpp>

View File

@@ -1,4 +1,7 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <catch2/catch.hpp>
#include <numeric>

View File

@@ -3,6 +3,7 @@ add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests_main.cpp
slic3r_jobs_tests.cpp
slic3r_version_tests.cpp
slic3r_arrangejob_tests.cpp
)
# mold linker for successful linking needs also to link TBB library and link it before libslic3r.

View File

@@ -0,0 +1,351 @@
#include "catch2/catch.hpp"
#include "test_utils.hpp"
#include <random>
#include "slic3r/GUI/Jobs/UIThreadWorker.hpp"
#include "slic3r/GUI/Jobs/BoostThreadWorker.hpp"
#include "slic3r/GUI/Jobs/ArrangeJob2.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/Format/3mf.hpp"
class RandomArrangeSettings: public Slic3r::arr2::ArrangeSettingsView {
Slic3r::arr2::ArrangeSettingsDb::Values m_v;
std::mt19937 m_rng;
public:
explicit RandomArrangeSettings(int seed) : m_rng(seed)
{
std::uniform_real_distribution<float> fdist(0., 100.f);
std::uniform_int_distribution<> bdist(0, 1);
std::uniform_int_distribution<> dist;
m_v.d_obj = fdist(m_rng);
m_v.d_bed = fdist(m_rng);
m_v.rotations = bdist(m_rng);
m_v.geom_handling = static_cast<GeometryHandling>(dist(m_rng) % ghCount);
m_v.arr_strategy = static_cast<ArrangeStrategy>(dist(m_rng) % asCount);
m_v.xl_align = static_cast<XLPivots>(dist(m_rng) % xlpCount);
}
explicit RandomArrangeSettings() : m_rng(std::random_device{} ()) {}
float get_distance_from_objects() const override { return m_v.d_obj; }
float get_distance_from_bed() const override { return m_v.d_bed; }
bool is_rotation_enabled() const override { return m_v.rotations; }
XLPivots get_xl_alignment() const override { return m_v.xl_align; }
GeometryHandling get_geometry_handling() const override { return m_v.geom_handling; }
ArrangeStrategy get_arrange_strategy() const override { return m_v.arr_strategy; }
};
TEMPLATE_TEST_CASE("Arranging empty bed should do nothing",
"[arrangejob][fillbedjob]",
Slic3r::GUI::ArrangeJob2,
Slic3r::GUI::FillBedJob2)
{
using namespace Slic3r;
using namespace Slic3r::GUI;
using JobType = TestType;
Model m;
UIThreadWorker w;
RandomArrangeSettings settings;
w.push(std::make_unique<JobType>(arr2::Scene{
arr2::SceneBuilder{}.set_model(m).set_arrange_settings(&settings)}));
w.process_events();
REQUIRE(m.objects.empty());
}
static void center_first_instance(Slic3r::ModelObject *mo,
const Slic3r::BoundingBox &bedbb)
{
using namespace Slic3r;
Vec2d d = unscaled(bedbb).center() -
to_2d(mo->instance_bounding_box(0).center());
auto tr = mo->instances.front()->get_transformation().get_matrix();
tr.translate(to_3d(d, 0.));
mo->instances.front()->set_transformation(Geometry::Transformation(tr));
}
TEST_CASE("Basic arrange with cube", "[arrangejob]") {
using namespace Slic3r;
using namespace Slic3r::GUI;
std::string basepath = TEST_DATA_DIR PATH_SEPARATOR;
DynamicPrintConfig cfg;
cfg.load_from_ini(basepath + "default_fff.ini",
ForwardCompatibilitySubstitutionRule::Enable);
Model m = Model::read_from_file(basepath + "20mm_cube.obj", &cfg);
UIThreadWorker w;
arr2::ArrangeSettings settings;
Points bedpts = get_bed_shape(cfg);
arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts);
SECTION("Single cube needs to be centered") {
w.push(std::make_unique<ArrangeJob2>(arr2::Scene{
arr2::SceneBuilder{}
.set_model(m)
.set_arrange_settings(&settings)
.set_bed(cfg)}));
w.process_events();
REQUIRE(m.objects.size() == 1);
REQUIRE(m.objects.front()->instances.size() == 1);
Vec3d c3 = m.objects.front()->bounding_box_exact().center();
Point c{scaled(c3.x()), scaled(c3.y())};
REQUIRE(c == bounding_box(bed).center());
}
SECTION("Selected cube needs to go beside existing") {
REQUIRE(m.objects.size() == 1);
ModelObject *mo = m.objects.front();
// Center the first instance within the bed
center_first_instance(mo, bounding_box(bed));
m.objects.front()->add_instance();
REQUIRE(m.objects.front()->instances.size() == 2);
arr2::FixedSelection sel({ {false, true} });
arr2::Scene scene{arr2::SceneBuilder{}
.set_model(m)
.set_arrange_settings(&settings)
.set_bed(cfg)
.set_selection(&sel)};
w.push(std::make_unique<ArrangeJob2>(std::move(scene)));
w.process_events();
auto bb0 = m.objects.front()->instance_bounding_box(0);
auto bb1 = m.objects.front()->instance_bounding_box(1);
REQUIRE(!bb0.contains(bb1));
bb0.merge(bb1);
Vec2d sz = to_2d(bb0.size());
if (sz.x() > sz.y())
std::swap(sz.x(), sz.y());
double d_obj = settings.get_distance_from_objects();
REQUIRE(sz.y() == Approx(2. * bb1.size().y() + d_obj));
}
SECTION("Selected cube (different object), needs to go beside existing") {
REQUIRE(m.objects.size() == 1);
ModelObject *mo = m.objects.front();
// Center the first instance within the bed
center_first_instance(mo, bounding_box(bed));
ModelObject *mosel = m.add_object(*m.objects.front());
arr2::FixedSelection sel({ {false}, {true} });
arr2::Scene scene{arr2::SceneBuilder{}
.set_model(m)
.set_arrange_settings(&settings)
.set_bed(cfg)
.set_selection(&sel)};
w.push(std::make_unique<ArrangeJob2>(std::move(scene)));
w.process_events();
auto bb0 = mo->instance_bounding_box(0);
auto bb1 = mosel->instance_bounding_box(0);
REQUIRE(!bb0.contains(bb1));
bb0.merge(bb1);
Vec2d sz = to_2d(bb0.size());
if (sz.x() > sz.y())
std::swap(sz.x(), sz.y());
double d_obj = settings.get_distance_from_objects();
REQUIRE(sz.y() == Approx(2. * bb1.size().y() + d_obj));
}
SECTION("Four cubes needs to touch each other after arrange") {
ModelObject *mo = m.objects.front();
mo->add_instance();
mo->add_instance();
mo->add_instance();
auto bedbb = unscaled<double>(bounding_box(bed));
ModelInstance *mi = mo->instances[0];
Vec2d d = bedbb.min - to_2d(mo->instance_bounding_box(0).center());
auto tr = mi->get_transformation().get_matrix();
tr.translate(to_3d(d, 0.));
mi->set_transformation(Geometry::Transformation(tr));
mi = mo->instances[1];
d = Vec2d(bedbb.min.x(), bedbb.max.y()) -
to_2d(mo->instance_bounding_box(1).center());
tr = mi->get_transformation().get_matrix();
tr.translate(to_3d(d, 0.));
mi->set_transformation(Geometry::Transformation(tr));
mi = mo->instances[2];
d = bedbb.max - to_2d(mo->instance_bounding_box(2).center());
tr = mi->get_transformation().get_matrix();
tr.translate(to_3d(d, 0.));
mi->set_transformation(Geometry::Transformation(tr));
mi = mo->instances[3];
d = Vec2d(bedbb.max.x(), bedbb.min.y()) -
to_2d(mo->instance_bounding_box(3).center());
tr = mi->get_transformation().get_matrix();
tr.translate(to_3d(d, 0.));
mi->set_transformation(Geometry::Transformation(tr));
arr2::Scene scene{arr2::SceneBuilder{}
.set_model(m)
.set_arrange_settings(&settings)
.set_bed(cfg)};
w.push(std::make_unique<ArrangeJob2>(std::move(scene)));
w.process_events();
auto pilebb = m.objects.front()->bounding_box_exact();
Vec3d c3 = pilebb.center();
Point c{scaled(c3.x()), scaled(c3.y())};
REQUIRE(c == bounding_box(bed).center());
float d_obj = settings.get_distance_from_objects();
REQUIRE(pilebb.size().x() == Approx(2. * 20. + d_obj));
REQUIRE(pilebb.size().y() == Approx(2. * 20. + d_obj));
}
}
struct DummyProgress: Slic3r::ProgressIndicator {
int range = 100;
int pr = 0;
std::string statustxt;
void set_range(int r) override { range = r; }
void set_cancel_callback(CancelFn = CancelFn()) override {}
void set_progress(int p) override { pr = p; }
void set_status_text(const char *txt) override { statustxt = txt; }
int get_range() const override { return range; }
};
TEST_CASE("Test for modifying model during arrangement", "[arrangejob][fillbedjob]")
{
using namespace Slic3r;
using namespace Slic3r::GUI;
std::string basepath = TEST_DATA_DIR PATH_SEPARATOR;
DynamicPrintConfig cfg;
cfg.load_from_ini(basepath + "default_fff.ini",
ForwardCompatibilitySubstitutionRule::Enable);
Model m;
ModelObject* new_object = m.add_object();
new_object->name = "20mm_cyl";
new_object->add_instance();
TriangleMesh mesh = make_cylinder(10., 10.);
ModelVolume* new_volume = new_object->add_volume(mesh);
new_volume->name = new_object->name;
Points bedpts = get_bed_shape(cfg);
arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts);
BoostThreadWorker w(std::make_unique<DummyProgress>());
RandomArrangeSettings settings;
SECTION("Remove 10 cylinder instances during arrange") {
for (size_t i = 1; i < 10; ++i)
new_object->add_instance();
arr2::Scene scene{arr2::SceneBuilder{}
.set_model(m)
.set_arrange_settings(&settings)
.set_bed(cfg)};
ArrangeJob2::Callbacks cbs;
cbs.on_prepared = [&m] (auto &) {
m.clear_objects();
};
w.push(std::make_unique<ArrangeJob2>(std::move(scene), cbs));
w.wait_for_current_job();
REQUIRE(m.objects.empty());
}
}
//TEST_CASE("Logical bed needs to be used when physical bed is full",
// "[arrangejob][fillbedjob]")
//{
// using namespace Slic3r;
// using namespace Slic3r::GUI;
// std::string basepath = TEST_DATA_DIR PATH_SEPARATOR;
// DynamicPrintConfig cfg;
// cfg.load_from_ini(basepath + "default_fff.ini",
// ForwardCompatibilitySubstitutionRule::Enable);
// Model m;
// ModelObject* new_object = m.add_object();
// new_object->name = "bigbox";
// new_object->add_instance();
// TriangleMesh mesh = make_cube(200., 200., 10.);
// ModelVolume* new_volume = new_object->add_volume(mesh);
// new_volume->name = new_object->name;
// Points bedpts = get_bed_shape(cfg);
// arr2::ArrangeBed bed = arr2::to_arrange_bed(bedpts);
// auto bedbb = bounding_box(bed);
// center_first_instance(new_object, bedbb);
// new_object = m.add_object();
// new_object->name = "40x20mm_box";
// new_object->add_instance();
// mesh = make_cube(50., 50., 50.);
// new_volume = new_object->add_volume(mesh);
// new_volume->name = new_object->name;
// UIThreadWorker w(std::make_unique<DummyProgress>());
// arr2::ArrangeSettings settings;
// SECTION("Single cube needs to be on first logical bed") {
// {
// arr2::Scene scene{&m, &settings, &cfg};
// w.push(std::make_unique<ArrangeJob2>(std::move(scene)));
// w.process_events();
// }
// store_3mf("logicalbed_10mm.3mf", &m, &cfg, false);
// REQUIRE(m.objects.size() == 2);
// Vec3d c3 = m.objects[1]->bounding_box_exact().center();
// Point result_center{scaled(c3.x()), scaled(c3.y())};
// auto bedidx_ojb1 = scene.virtual_bed_handler().get_bed_index(m.objects[1]->instances[0]);
// REQUIRE(bedidx_ojb1 == 1);
// }
//}

View File

@@ -3,6 +3,7 @@
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/Format/OBJ.hpp>
#include <random>
#if defined(WIN32) || defined(_WIN32)
#define PATH_SEPARATOR R"(\)"
@@ -18,4 +19,22 @@ inline Slic3r::TriangleMesh load_model(const std::string &obj_filename)
return mesh;
}
template<class T>
Slic3r::FloatingOnly<T> random_value(T minv, T maxv)
{
static std::mt19937 rng(std::random_device{}());
std::uniform_real_distribution<T> dist(minv, maxv);
return dist(rng);
}
template<class T>
Slic3r::IntegerOnly<T> random_value(T minv, T maxv)
{
static std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<T> dist(minv, maxv);
return dist(rng);
}
#endif // SLIC3R_TEST_UTILS