From 7c58218149d62943e35a80dc4f01ea8b02dafc70 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 2 Sep 2018 13:29:07 -0500 Subject: [PATCH] Support zip-deflated AMF files in libslic3r. Includes basic tests. Implements #4511 --- src/CMakeLists.txt | 1 + src/test/inputs/test_amf/20mmbox.amf | 138 ++++++++++++++++++ .../20mmbox_deflated-in_directories.amf | Bin 0 -> 629 bytes .../test_amf/20mmbox_deflated-mult_files.amf | Bin 0 -> 1220 bytes src/test/inputs/test_amf/20mmbox_deflated.amf | Bin 0 -> 619 bytes src/test/libslic3r/test_amf.cpp | 60 ++++++++ xs/src/libslic3r/IO/AMF.cpp | 26 +++- 7 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 src/test/inputs/test_amf/20mmbox.amf create mode 100644 src/test/inputs/test_amf/20mmbox_deflated-in_directories.amf create mode 100644 src/test/inputs/test_amf/20mmbox_deflated-mult_files.amf create mode 100644 src/test/inputs/test_amf/20mmbox_deflated.amf create mode 100644 src/test/libslic3r/test_amf.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a2bb974cb..5d0b61e340 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -266,6 +266,7 @@ set(SLIC3R_TEST_SOURCES ${TESTDIR}/libslic3r/test_test_data.cpp ${TESTDIR}/libslic3r/test_geometry.cpp ${TESTDIR}/libslic3r/test_gcodewriter.cpp + ${TESTDIR}/libslic3r/test_amf.cpp ) add_executable(slic3r slic3r.cpp) diff --git a/src/test/inputs/test_amf/20mmbox.amf b/src/test/inputs/test_amf/20mmbox.amf new file mode 100644 index 0000000000..c945a45cdd --- /dev/null +++ b/src/test/inputs/test_amf/20mmbox.amf @@ -0,0 +1,138 @@ + + + Slic3r 1.3.1-dev + + 20mmbox.stl + + + + + 10 + 10 + 0 + + + + + -10 + -10 + 0 + + + + + -10 + 10 + 0 + + + + + 10 + -10 + 0 + + + + + 10 + -10 + 10 + + + + + -10 + 10 + 10 + + + + + -10 + -10 + 10 + + + + + 10 + 10 + 10 + + + + + 20mmbox.stl + + 0 + 1 + 2 + + + 1 + 0 + 3 + + + 4 + 5 + 6 + + + 5 + 4 + 7 + + + 0 + 4 + 3 + + + 4 + 0 + 7 + + + 4 + 1 + 3 + + + 1 + 4 + 6 + + + 5 + 1 + 6 + + + 1 + 5 + 2 + + + 5 + 0 + 2 + + + 0 + 5 + 7 + + + + + + + 67.5 + 35 + 0 + 1 + + + diff --git a/src/test/inputs/test_amf/20mmbox_deflated-in_directories.amf b/src/test/inputs/test_amf/20mmbox_deflated-in_directories.amf new file mode 100644 index 0000000000000000000000000000000000000000..4f8a63607c2657d085e2319d0bfe47842b9ceac7 GIT binary patch literal 629 zcmWIWW@Zs#U|`^2_?)HWd;P$HNBbEW7!GhVFbFWnFeIjA7U>%qWK#7(h6<{MwZvi3=Cl6Y}nbnW&@sm-!&KA;a>73y?w&HZ%nu46nJk=(>%bE z>H7OivGdCXrKX`F!s1hE{QlH#Yfjy_&e*$b*BYOR>Qh(+RjtBZvp)5=?AvSSKmSFP z&yF+qPMVqPEu6XWTFR}Lle#z+Wu_TT^%L6u{~Bkg(b7jpr9`K5lfK9A%>H)w-SP6)yJ^3^pSyeaYJAbsi9g$~#)j4$@2lsisea&Nz>r>RAZE<) zxbOIfTTgw(+BUjKc-u6j_HEQLnD&%m8tXBm;}K!Z5f^qX(BVHX5j9 z@(kb+%@I>ysavYNeYv38Cjqx2!=H=+-i%Cg%(#+*1Tcv(FaT2q!;(f23o~J`LJ|gA Vq6qM2Wdo^a1i~;N&B+Af0RX(I1Cam# literal 0 HcmV?d00001 diff --git a/src/test/inputs/test_amf/20mmbox_deflated-mult_files.amf b/src/test/inputs/test_amf/20mmbox_deflated-mult_files.amf new file mode 100644 index 0000000000000000000000000000000000000000..f334baf9de34f217060402d6c2df76d861aefe64 GIT binary patch literal 1220 zcmWIWW@Zs#U|`^2aLH2gy?)@pqy3Bw3o>ZbCdEb^b&K^LPIzinBz5j zqEBe`M3+`@GcdBeW@caj6KBKD<~1Ae?E9{{=nnUiFX`mQ2^* zUy7YyE+{n(4G|WfQseihc3X4mzIDdlWxLk+OjMu3DyV7|?wa+fzh&QEJOBAFqI`Co zxp&gcTyNpbjn`6cy`0p=sVFneXsVyk_W##7LyeX`Iw~bPy@j>#z_OM1K5npmx?W_q z{#qHsOef{{Pj30@8T%g9Ikm|*kbUarTJe8QUvKIpGBa8%F1gC2yX-X|(+sUHA*adf z%CF4MI;bRfZTg3MhktwjP++kAGPiw4xm=v)&iLD3+IGBLakba!&I;r5L#j={924AWST86A%ZV~)76Yk>~`d5Ng$iW?W`ypWa_;{J8) zLXYj9Ul}^i5f>sZOt*Qm-X+;_wS5a~?a2(C`hA8Q%4a>?c-}NwnSZwCmGz+$!~LSQ zWql6T>7~An$vU7^ywtOJkM-sz)2r7`M(KnzuCUIn+P1eN`t;71pD+1KKHYiuvzKQ8 zk7$mV`bynW7!&@ka1e07?O)sbKh}0!Ah|W?W@~ z1h9-?U;q{k3`-h8ER>Rg6;d*wl?nmg2m>(_7P5gXOmG8X$qZ;DmLwM7&B_L{oe2n+ K0BJ535Dx$h{QTqq literal 0 HcmV?d00001 diff --git a/src/test/inputs/test_amf/20mmbox_deflated.amf b/src/test/inputs/test_amf/20mmbox_deflated.amf new file mode 100644 index 0000000000000000000000000000000000000000..f0293fcd1f7ac8f5ce3178aac104399b605b36ef GIT binary patch literal 619 zcmWIWW@Zs#U|`^2aLH2gy?)@pqy3Bw3o>ZbCdEb^b&K^LPIzinBz5j zqCvQ{f}4Sn>yIKegMMQ}?Yi_AcAC#%H4X6jni1t8mw>PyH?X_S*T+e-Y)g1<6X+~51gtq^`#u;j~^wCi%(djL$g$I_cy!UZ~?bG!lv-Q`? z7-l*tzkhPeSI^k@sLrWPzJcshH`j{)bNYHyCy|-aVsXh;Cf#MP`Iu&CbqP65URQo) zcGf{9xoguu+&lc+`-cL9?U%XjJIdwaGN#qvANUwBq}LjV88bZYJO1I; zQ(v*RjV==2HVvtL8+8n(J!P22dd%o}L>P0#gofz&Htu5HE9nq(EzWjX2U-IeByPv&019(Jp z#MD>nmMU*wE~xfNz^%yeCu4v&Ba<96u2diaOdSjiz(m2Yq!GkINfWG)G=Y{L0=!w- PKuQ^bFc3(yFoAdg-p2ir literal 0 HcmV?d00001 diff --git a/src/test/libslic3r/test_amf.cpp b/src/test/libslic3r/test_amf.cpp new file mode 100644 index 0000000000..ad041e9769 --- /dev/null +++ b/src/test/libslic3r/test_amf.cpp @@ -0,0 +1,60 @@ +#include +#include +#include "IO.hpp" + +using namespace Slic3r; +using namespace std::literals::string_literals; + +SCENARIO("Reading deflated AMF files") { + GIVEN("Compressed AMF file of a 20mm cube") { + Model model; + WHEN("file is read") { + bool result_code = IO::AMF::read(std::string(testfile_dir) + "test_amf/20mmbox_deflated.amf"s, &model); + THEN("Does not return false.") { + REQUIRE(result_code == true); + } + THEN("Model object contains a single ModelObject.") { + REQUIRE(model.objects.size() == 1); + } + } + WHEN("single file is read with some subdirectories") { + bool result_code = IO::AMF::read(std::string(testfile_dir) + "test_amf/20mmbox_deflated-in_directories.amf"s, &model); + THEN("Read returns false.") { + REQUIRE(result_code == true); + } + THEN("Model object contains no ModelObjects.") { + REQUIRE(model.objects.size() == 1); + } + } + WHEN("file is read with unsupported file structure (multiple files)") { + bool result_code = IO::AMF::read(std::string(testfile_dir) + "test_amf/20mmbox_deflated-mult_files.amf"s, &model); + THEN("Read returns false.") { + REQUIRE(result_code == false); + } + THEN("Model object contains no ModelObjects.") { + REQUIRE(model.objects.size() == 0); + } + } + } + GIVEN("Uncompressed AMF file of a 20mm cube") { + Model model; + WHEN("file is read") { + bool result_code = IO::AMF::read(std::string(testfile_dir) + "test_amf/20mmbox.amf"s, &model); + THEN("Does not return false.") { + REQUIRE(result_code == true); + } + THEN("Model object contains a single ModelObject.") { + REQUIRE(model.objects.size() == 1); + } + } + WHEN("nonexistant file is read") { + bool result_code = IO::AMF::read(std::string(testfile_dir) + "test_amf/20mmbox-doesnotexist.amf"s, &model); + THEN("Read returns false.") { + REQUIRE(result_code == false); + } + THEN("Model object contains no ModelObject.") { + REQUIRE(model.objects.size() == 0); + } + } + } +} diff --git a/xs/src/libslic3r/IO/AMF.cpp b/xs/src/libslic3r/IO/AMF.cpp index 7433762ee9..967fb49c7d 100644 --- a/xs/src/libslic3r/IO/AMF.cpp +++ b/xs/src/libslic3r/IO/AMF.cpp @@ -1,4 +1,5 @@ #include "../IO.hpp" +#include "Zip/ZipArchive.hpp" #include #include #include @@ -450,15 +451,31 @@ void AMFParserContext::endDocument() bool AMF::read(std::string input_file, Model* model) { + ZipArchive zip_archive(input_file, 'R'); + auto file_list = zip_archive.list_entries(); + if (file_list.size() > 1) { + return false; // only support single file archives + } + std::string deflated_input_file {""}; + if (file_list.size() == 1) { + // extract the file with ZipArchive + if(!zip_archive.extract_entry(file_list[0], "3dmodel.xml")) + return false; + deflated_input_file = "3dmodel.xml"; + } else { + // not a deflated file + deflated_input_file = input_file; + } + XML_Parser parser = XML_ParserCreate(NULL); // encoding if (! parser) { printf("Couldn't allocate memory for parser\n"); return false; } - boost::nowide::ifstream fin(input_file, std::ios::in); + boost::nowide::ifstream fin(deflated_input_file, std::ios::in); if (!fin.is_open()) { - boost::nowide::cerr << "Cannot open file: " << input_file << std::endl; + boost::nowide::cerr << "Cannot open file: " << deflated_input_file << std::endl; return false; } @@ -489,6 +506,11 @@ AMF::read(std::string input_file, Model* model) XML_ParserFree(parser); fin.close(); + if (file_list.size() == 1) { + // Remove the extracted 3dmodel.xml file. + if (remove("3dmodel.xml") != 0) + return false; + } if (result) ctx.endDocument();