restore most loop orientation to ccw.

add test_model and test_print tests. Work is needed for them
This commit is contained in:
supermerill 2019-04-02 19:18:01 +02:00
parent 4e1e4cff73
commit c03564f218
14 changed files with 152 additions and 84 deletions

View File

@ -89,7 +89,7 @@ Then `cd` into the `deps` directory and use these commands to build:
You can also use the Visual Studio GUI or other generators as mentioned above. You can also use the Visual Studio GUI or other generators as mentioned above.
The `DESTDIR` option is the location where the bundle will be installed. The `DESTDIR` option is the location where the bundle will be installed.
This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory. This may be customized. If you leave it empty, the `DESTDIR` will be places inside the same `build` directory. For using your newly compile deps, use -DCMAKE_INSTALL_PREFIX=<path_of_build\destdir\usr\local> in your cmake command for slic3r.
Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build
become too long and the build might fail due to file writing errors. For this reason, it is recommended to become too long and the build might fail due to file writing errors. For this reason, it is recommended to

View File

@ -473,7 +473,10 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons
ExtrusionPath path(role, mm3_per_mm, width, height); ExtrusionPath path(role, mm3_per_mm, width, height);
path.polyline.points = std::move(poly.points); path.polyline.points = std::move(poly.points);
path.polyline.points.push_back(path.polyline.points.front()); path.polyline.points.push_back(path.polyline.points.front());
dst.emplace_back(new ExtrusionLoop(std::move(path))); ExtrusionLoop *loop = new ExtrusionLoop(std::move(path));
//default to ccw
loop->make_counter_clockwise();
dst.emplace_back(loop);
} }
} }
loops.clear(); loops.clear();

View File

@ -2022,6 +2022,17 @@ std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std:
std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid) std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::string &description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
{ {
#if DEBUG_EXTRUSION_OUTPUT
std::cout << "extrude loop_" << (original_loop.polygon().is_counter_clockwise() ? "ccw" : "clw") << ": ";
for (const ExtrusionPath &path : original_loop.paths) {
std::cout << ", path{ ";
for (const Point &pt : path.polyline.points) {
std::cout << ", " << floor(100 * unscale<double>(pt.x())) / 100.0 << ":" << floor(100 * unscale<double>(pt.y())) / 100.0;
}
std::cout << "}";
}
std::cout << "\n";
#endif
// get a copy; don't modify the orientation of the original loop object otherwise // get a copy; don't modify the orientation of the original loop object otherwise
// next copies (if any) would not detect the correct orientation // next copies (if any) would not detect the correct orientation
ExtrusionLoop loop = original_loop; ExtrusionLoop loop = original_loop;

View File

@ -670,45 +670,49 @@ void ModelObject::assign_new_unique_ids_recursive()
// return new ModelObject(parent, *this, true); // return new ModelObject(parent, *this, true);
//} //}
ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh) ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh, bool centered)
{ {
ModelVolume* v = new ModelVolume(this, mesh); ModelVolume* v = new ModelVolume(this, mesh);
this->volumes.push_back(v); this->volumes.push_back(v);
#if ENABLE_VOLUMES_CENTERING_FIXES #if ENABLE_VOLUMES_CENTERING_FIXES
v->center_geometry(); if(centered)
v->center_geometry();
#endif // ENABLE_VOLUMES_CENTERING_FIXES #endif // ENABLE_VOLUMES_CENTERING_FIXES
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh) ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh, bool centered)
{ {
ModelVolume* v = new ModelVolume(this, std::move(mesh)); ModelVolume* v = new ModelVolume(this, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
#if ENABLE_VOLUMES_CENTERING_FIXES #if ENABLE_VOLUMES_CENTERING_FIXES
v->center_geometry(); if (centered)
v->center_geometry();
#endif // ENABLE_VOLUMES_CENTERING_FIXES #endif // ENABLE_VOLUMES_CENTERING_FIXES
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
ModelVolume* ModelObject::add_volume(const ModelVolume &other) ModelVolume* ModelObject::add_volume(const ModelVolume &other, bool centered)
{ {
ModelVolume* v = new ModelVolume(this, other); ModelVolume* v = new ModelVolume(this, other);
this->volumes.push_back(v); this->volumes.push_back(v);
#if ENABLE_VOLUMES_CENTERING_FIXES #if ENABLE_VOLUMES_CENTERING_FIXES
v->center_geometry(); if (centered)
v->center_geometry();
#endif // ENABLE_VOLUMES_CENTERING_FIXES #endif // ENABLE_VOLUMES_CENTERING_FIXES
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh) ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh, bool centered)
{ {
ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
#if ENABLE_VOLUMES_CENTERING_FIXES #if ENABLE_VOLUMES_CENTERING_FIXES
v->center_geometry(); if (centered)
v->center_geometry();
#endif // ENABLE_VOLUMES_CENTERING_FIXES #endif // ENABLE_VOLUMES_CENTERING_FIXES
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;

View File

@ -193,10 +193,10 @@ public:
Model* get_model() { return m_model; }; Model* get_model() { return m_model; };
const Model* get_model() const { return m_model; }; const Model* get_model() const { return m_model; };
ModelVolume* add_volume(const TriangleMesh &mesh); ModelVolume* add_volume(const TriangleMesh &mesh, bool centered = true);
ModelVolume* add_volume(TriangleMesh &&mesh); ModelVolume* add_volume(TriangleMesh &&mesh, bool centered = true);
ModelVolume* add_volume(const ModelVolume &volume); ModelVolume* add_volume(const ModelVolume &volume, bool centered = true);
ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh); ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh, bool centered = true);
void delete_volume(size_t idx); void delete_volume(size_t idx);
void clear_volumes(); void clear_volumes();
bool is_multiparts() const { return volumes.size() > 1; } bool is_multiparts() const { return volumes.size() > 1; }

View File

@ -744,6 +744,10 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
// let's get it from the sorted collection as it might have been reversed // let's get it from the sorted collection as it might have been reversed
size_t i = idx - sorted_coll.orig_indices.begin(); size_t i = idx - sorted_coll.orig_indices.begin();
entities.append(*sorted_coll.entities[i]); entities.append(*sorted_coll.entities[i]);
//if thin extrusion is a loop, make it ccw like a normal contour.
if (ExtrusionLoop* loop = dynamic_cast<ExtrusionLoop*>(entities.entities.back())) {
loop->make_counter_clockwise();
}
} else { } else {
const PerimeterGeneratorLoop &loop = loops[*idx]; const PerimeterGeneratorLoop &loop = loops[*idx];
ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]); ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]);

View File

@ -1720,6 +1720,8 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio
first_layer_height // this will be overridden at G-code export time first_layer_height // this will be overridden at G-code export time
))); )));
eloop.paths.back().polyline = loop.split_at_first_point(); eloop.paths.back().polyline = loop.split_at_first_point();
//we make it clowkwise, but as it will be reversed, it will be ccw
eloop.make_clockwise();
out.append(eloop); out.append(eloop);
if (m_config.min_skirt_length.value > 0) { if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm. // The skirt length is limited. Sum the total amount of filament length extruded, in mm.

View File

@ -57,4 +57,5 @@
#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG) #define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG)
#define DEBUG_EXTRUSION_OUTPUT 0
#endif // _technologies_h_ #endif // _technologies_h_

View File

@ -1826,6 +1826,22 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
stl_get_size(&lower->stl); stl_get_size(&lower->stl);
} }
Pointf3s TriangleMesh::vertices()
{
Pointf3s tmp{};
if (this->repaired) {
if (this->stl.v_shared == nullptr)
stl_generate_shared_vertices(&stl); // build the list of vertices
for (auto i = 0; i < this->stl.stats.shared_vertices; i++) {
const auto& v = this->stl.v_shared[i];
tmp.emplace_back(Vec3d(v.x(), v.y(), v.z()));
}
} else {
BOOST_LOG_TRIVIAL(warning) << "TriangleMesh", "vertices() requires repair()";
}
return tmp;
}
// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. // Generate the vertex list for a cube solid of arbitrary size in X/Y/Z.
TriangleMesh make_cube(double x, double y, double z) { TriangleMesh make_cube(double x, double y, double z) {
Vec3d pv[8] = { Vec3d pv[8] = {

View File

@ -77,6 +77,9 @@ public:
stl_file stl; stl_file stl;
bool repaired; bool repaired;
/// --- for tests ----- ///
Pointf3s vertices();
private: private:
void require_shared_vertices(); void require_shared_vertices();

View File

@ -15,6 +15,8 @@ set(SLIC3R_TEST_SOURCES
libslic3r/test_flow.cpp libslic3r/test_flow.cpp
libslic3r/test_gcodewriter.cpp libslic3r/test_gcodewriter.cpp
libslic3r/test_geometry.cpp libslic3r/test_geometry.cpp
libslic3r/test_model.cpp
libslic3r/test_print.cpp
) )
if (NOT TARGET Catch) if (NOT TARGET Catch)

View File

@ -42,8 +42,8 @@ SCENARIO("Extrusion width specifics", "[!mayfail]") {
std::string gcode_filepath(""); std::string gcode_filepath("");
Slic3r::Test::gcode(gcode_filepath, print); Slic3r::Test::gcode(gcode_filepath, print);
GCodeReader parser {Slic3r::GCodeReader()}; GCodeReader parser {Slic3r::GCodeReader()};
const auto layer_height { config->opt_float("layer_height") }; const double layer_height = config->opt_float("layer_height");
std::string gcode_from_file{ read_to_string(gcode_filepath) }; std::string gcode_from_file= read_to_string(gcode_filepath);
parser.parse_buffer(gcode_from_file, [&E_per_mm_bottom, layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) parser.parse_buffer(gcode_from_file, [&E_per_mm_bottom, layer_height] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
{ {
if (self.z() == Approx(layer_height).margin(0.01)) { // only consider first layer if (self.z() == Approx(layer_height).margin(0.01)) { // only consider first layer

View File

@ -1,47 +1,55 @@
#include <catch.hpp> #include <catch.hpp>
#include "Model.hpp" #include "../../libslic3r/Config.hpp"
#include "test_data.hpp" // get access to init_print, etc #include "../../libslic3r/Model.hpp"
#include "../test_data.hpp" // get access to init_print, etc
using namespace Slic3r;
using namespace Slic3r::Test; using namespace Slic3r::Test;
SCENARIO("Model construction") { SCENARIO("Model construction") {
GIVEN("A Slic3r Model") { GIVEN("A Slic3r Model") {
auto model {Slic3r::Model()}; Model model;
auto sample_mesh {Slic3r::TriangleMesh::make_cube(20,20,20)}; TriangleMesh sample_mesh = make_cube(20,20,20);
sample_mesh.repair(); sample_mesh.repair();
auto config {Slic3r::Config::new_from_defaults()}; DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults();
std::shared_ptr<Slic3r::Print> print = std::make_shared<Slic3r::Print>(); Slic3r::Print print;
print->apply_config(config); print.apply_config(*config);
//Slic3r::Test::init_print(print, { sample_mesh }, model, config);
WHEN("Model object is added") { WHEN("Model object is added") {
ModelObject* mo {model.add_object()}; ModelObject* mo = model.add_object();
mo->name = "cube20";
THEN("Model object list == 1") { THEN("Model object list == 1") {
REQUIRE(model.objects.size() == 1); REQUIRE(model.objects.size() == 1);
} }
mo->add_volume(sample_mesh); mo->add_volume(sample_mesh, false);
THEN("Model volume list == 1") { THEN("Model volume list == 1") {
REQUIRE(mo->volumes.size() == 1); REQUIRE(mo->volumes.size() == 1);
} }
THEN("Model volume modifier is false") { THEN("Model volume modifier is false") {
REQUIRE(mo->volumes.front()->modifier == false); REQUIRE(mo->volumes.front()->is_modifier() == false);
} }
THEN("Mesh is equivalent to input mesh.") { THEN("Mesh is equivalent to input mesh.") {
REQUIRE(sample_mesh.vertices() == mo->volumes.front()->mesh.vertices()); REQUIRE(sample_mesh.vertices() == mo->volumes.front()->mesh.vertices());
} }
ModelInstance* inst {mo->add_instance()}; ModelInstance* inst = mo->add_instance();
inst->rotation = 0; inst->set_rotation(Vec3d(0,0,0));
inst->scaling_factor = 1.0; inst->set_scaling_factor(Vec3d(1, 1, 1));
model.arrange_objects(print->config.min_object_distance()); model.arrange_objects(print.config().min_object_distance());
model.center_instances_around_point(Slic3r::Pointf(100,100)); model.center_instances_around_point(Slic3r::Vec2d(100,100));
print->auto_assign_extruders(mo); print.auto_assign_extruders(mo);
print->add_model_object(mo); //print.add_model_object(mo);
print.apply(model, *config);
print.validate();
THEN("Print works?") { THEN("Print works?") {
print->process(); std::string gcode_filepath("");
auto gcode {std::stringstream("")}; Slic3r::Test::gcode(gcode_filepath, print);
print->export_gcode(gcode, true); std::cout << "gcode generation done\n";
REQUIRE(gcode.str().size() > 0); std::string gcode_from_file = read_to_string(gcode_filepath);
REQUIRE(gcode_from_file.size() > 0);
clean_file(gcode_filepath, "gcode");
} }
} }

View File

@ -1,60 +1,70 @@
#include <catch.hpp> #include <catch.hpp>
#include <string> #include <string>
#include "test_data.hpp" #include "../test_data.hpp"
#include "libslic3r.h" #include "../../libslic3r/libslic3r.h"
using namespace Slic3r::Test; using namespace Slic3r::Test;
using namespace Slic3r;
using namespace std::literals; using namespace std::literals;
SCENARIO("PrintObject: Perimeter generation") { SCENARIO("PrintObject: Perimeter generation") {
GIVEN("20mm cube and default config") { GIVEN("20mm cube and default config") {
auto config {Slic3r::Config::new_from_defaults()}; DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults();
TestMesh m { TestMesh::cube_20x20x20 }; TestMesh m = TestMesh::cube_20x20x20;
Slic3r::Model model; Model model;
auto event_counter {0U}; unsigned int event_counter = 0U;
std::string stage; std::string stage;
int value {0}; int value = 0;
auto callback {[&event_counter, &stage, &value] (int a, const char* b) { stage = std::string(b); event_counter++; value = a; }}; auto callback {[&event_counter, &stage, &value] (int a, const char* b) { stage = std::string(b); event_counter++; value = a; }};
config->set("fill_density", 0); config->set_key_value("fill_density", new ConfigOptionPercent(0));
WHEN("make_perimeters() is called") { WHEN("make_perimeters() is called") {
auto print {Slic3r::Test::init_print({m}, model, config)}; Print print;
const auto& object = *(print->objects.at(0)); Slic3r::Test::init_print(print, { m }, model, config);
print->objects[0]->make_perimeters(); PrintObject& object = *(print.objects().at(0));
print.process();
// there are 66.66666.... layers for 0.3mm in 20mm
//slic3r is rounded (slice at half-layer), slic3rPE is less?
//TODO: check the slic32r why it's not cut at half-layer
THEN("67 layers exist in the model") { THEN("67 layers exist in the model") {
REQUIRE(object.layers.size() == 67); REQUIRE(object.layers().size() == 67);
} }
THEN("Every layer in region 0 has 1 island of perimeters") { THEN("Every layer in region 0 has 1 island of perimeters") {
for(auto* layer : object.layers) { for(Layer* layer : object.layers()) {
REQUIRE(layer->regions[0]->perimeters.size() == 1); REQUIRE(layer->regions()[0]->perimeters.entities.size() == 1);
} }
} }
THEN("Every layer in region 0 has 3 paths in its perimeters list.") { THEN("Every layer (but top) in region 0 has 3 paths in its perimeters list.") {
for(auto* layer : object.layers) { LayerPtrs layers = object.layers();
REQUIRE(layer->regions[0]->perimeters.items_count() == 3); for (auto layer = layers.begin(); layer != layers.end() - 1; ++layer) {
REQUIRE((*layer)->regions()[0]->perimeters.items_count() == 3);
} }
} }
THEN("Top layer in region 0 has 1 path in its perimeters list (only 1 perimeter on top).") {
REQUIRE(object.layers().back()->regions()[0]->perimeters.items_count() == 1);
}
} }
} }
} }
SCENARIO("Print: Skirt generation") { SCENARIO("Print: Skirt generation") {
GIVEN("20mm cube and default config") { GIVEN("20mm cube and default config") {
auto config {Slic3r::Config::new_from_defaults()}; DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults();
TestMesh m { TestMesh::cube_20x20x20 }; TestMesh m = TestMesh::cube_20x20x20;
Slic3r::Model model; Slic3r::Model model;
auto event_counter {0U}; unsigned int event_counter = 0U;
std::string stage; std::string stage;
int value {0}; int value = 0;
config->set("skirt_height", 1); config->set_key_value("skirt_height", new ConfigOptionInt(1));
config->set("skirt_distance", 1); config->set_key_value("skirt_distance", new ConfigOptionFloat(1));
WHEN("Skirts is set to 2 loops") { WHEN("Skirts is set to 2 loops") {
config->set("skirts", 2); config->set_key_value("skirts", new ConfigOptionInt(2));
auto print {Slic3r::Test::init_print({m}, model, config)}; Print print;
print->make_skirt(); Slic3r::Test::init_print(print, { m }, model, config);
print.process();
THEN("Skirt Extrusion collection has 2 loops in it") { THEN("Skirt Extrusion collection has 2 loops in it") {
REQUIRE(print->skirt.items_count() == 2); REQUIRE(print.skirt().items_count() == 2);
REQUIRE(print->skirt.flatten().entities.size() == 2); REQUIRE(print.skirt().flatten().entities.size() == 2);
} }
} }
} }
@ -62,36 +72,40 @@ SCENARIO("Print: Skirt generation") {
SCENARIO("Print: Brim generation") { SCENARIO("Print: Brim generation") {
GIVEN("20mm cube and default config, 1mm first layer width") { GIVEN("20mm cube and default config, 1mm first layer width") {
auto config {Slic3r::Config::new_from_defaults()}; DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults();
TestMesh m { TestMesh::cube_20x20x20 }; TestMesh m = TestMesh::cube_20x20x20;
Slic3r::Model model; Slic3r::Model model;
auto event_counter {0U}; unsigned int event_counter = 0U;
std::string stage; std::string stage;
int value {0}; int value = 0;
config->set("first_layer_extrusion_width", 1); config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(1, false));
WHEN("Brim is set to 3mm") { WHEN("Brim is set to 3mm") {
config->set("brim_width", 3); config->set_key_value("brim_width", new ConfigOptionFloat(3));
auto print {Slic3r::Test::init_print({m}, model, config)}; Print print;
print->make_brim(); Slic3r::Test::init_print(print, { m }, model, config);
print.process();
THEN("Brim Extrusion collection has 3 loops in it") { THEN("Brim Extrusion collection has 3 loops in it") {
REQUIRE(print->brim.items_count() == 3); REQUIRE(print.brim().items_count() == 3);
} }
} }
WHEN("Brim is set to 6mm") { WHEN("Brim is set to 6mm") {
config->set("brim_width", 6); config->set_key_value("brim_width", new ConfigOptionFloat(6));
auto print {Slic3r::Test::init_print({m}, model, config)}; Print print;
print->make_brim(); Slic3r::Test::init_print(print, { m }, model, config);
print.process();
THEN("Brim Extrusion collection has 6 loops in it") { THEN("Brim Extrusion collection has 6 loops in it") {
REQUIRE(print->brim.items_count() == 6); REQUIRE(print.brim().items_count() == 6);
} }
} }
WHEN("Brim is set to 6mm, extrusion width 0.5mm") { WHEN("Brim is set to 6mm, extrusion width 0.5mm") {
config->set("brim_width", 6); config->set_key_value("brim_width", new ConfigOptionFloat(6));
config->set("first_layer_extrusion_width", 0.5); config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false));
auto print {Slic3r::Test::init_print({m}, model, config)}; Print print;
print->make_brim(); Slic3r::Test::init_print(print, { m }, model, config);
THEN("Brim Extrusion collection has 12 loops in it") { print.process();
REQUIRE(print->brim.items_count() == 12); double nbLoops = 6.0 / print.brim_flow().spacing();
THEN("Brim Extrusion collection has " + std::to_string(nbLoops) + " loops in it (flow="+ std::to_string(print.brim_flow().spacing())+")") {
REQUIRE(print.brim().items_count() == floor(nbLoops));
} }
} }
} }