diff --git a/build_release_macos.sh b/build_release_macos.sh index db03cb4ecd..728cb8e757 100755 --- a/build_release_macos.sh +++ b/build_release_macos.sh @@ -104,8 +104,8 @@ fi CMAKE_VERSION=$(cmake --version | head -1 | sed 's/[^0-9]*\([0-9]*\).*/\1/') if [ "$CMAKE_VERSION" -ge 4 ] 2>/dev/null; then - export CMAKE_POLICY_VERSION_MINIMUM=3.5 - export CMAKE_POLICY_COMPAT="-DCMAKE_POLICY_VERSION_MINIMUM=3.5" + export CMAKE_POLICY_VERSION_MINIMUM=3.10 + export CMAKE_POLICY_COMPAT="-DCMAKE_POLICY_VERSION_MINIMUM=3.10" echo "Detected CMake 4.x, adding compatibility flag (env + cmake arg)" else export CMAKE_POLICY_COMPAT="" diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index aa64ad4dc9..eda0c67e94 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,5 +1,5 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "4.0") - set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE) + set(CMAKE_POLICY_VERSION_MINIMUM 3.10 CACHE STRING "" FORCE) endif() # @@ -24,7 +24,7 @@ endif() # therefore, unfortunately, the installation cannot be copied/moved elsewhere without re-installing wxWidgets. # -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) if (APPLE) # if CMAKE_OSX_DEPLOYMENT_TARGET is not set, set it to 12.0 (the lowest Xcode 27 accepts) if (NOT CMAKE_OSX_DEPLOYMENT_TARGET) @@ -224,7 +224,7 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE) ${_source_dir_arg} ${_gen} CMAKE_ARGS - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR} -DCMAKE_MODULE_PATH:STRING=${PROJECT_SOURCE_DIR}/../cmake/modules -DCMAKE_PREFIX_PATH:STRING=${DESTDIR} @@ -279,7 +279,7 @@ else() ${_source_dir_arg} ${_gen} CMAKE_ARGS - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR} -DCMAKE_PREFIX_PATH:STRING=${DESTDIR} -DCMAKE_IGNORE_PREFIX_PATH:STRING=${CMAKE_IGNORE_PREFIX_PATH} diff --git a/deps/OCCT/0001-BRepMesh-seam-pcurve-at-edge-parameter.patch b/deps/OCCT/0001-BRepMesh-seam-pcurve-at-edge-parameter.patch new file mode 100644 index 0000000000..84c599a5f3 --- /dev/null +++ b/deps/OCCT/0001-BRepMesh-seam-pcurve-at-edge-parameter.patch @@ -0,0 +1,28 @@ +diff --git a/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_ModelPreProcessor.cxx b/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_ModelPreProcessor.cxx +index 6f63781..9b1c08e 100644 +--- a/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_ModelPreProcessor.cxx ++++ b/src/ModelingAlgorithms/TKMesh/BRepMesh/BRepMesh_ModelPreProcessor.cxx +@@ -210,10 +210,10 @@ private: + + // Define two pcurves of the seam-edge. + occ::handle aPC1, aPC2; +- double af, al; ++ double af, al, af1, al1; + + aE.Orientation(TopAbs_FORWARD); +- aPC1 = BRep_Tool::CurveOnSurface(aE, aF, af, al); ++ aPC1 = BRep_Tool::CurveOnSurface(aE, aF, af1, al1); + + aE.Orientation(TopAbs_REVERSED); + aPC2 = BRep_Tool::CurveOnSurface(aE, aF, af, al); +@@ -224,7 +224,9 @@ private: + } + + // Select the correct pcurve of the seam-edge. +- const gp_Pnt2d& aFPntOfPC1 = aPC1->Value(aPC1->FirstParameter()); ++ // Use the edge's first parameter. A Geom2d_Line's FirstParameter() is -Precision::Infinite(), ++ // where a direction of (2e-16, -1) from rounding error gives an X far outside the U range. ++ const gp_Pnt2d aFPntOfPC1 = aPC1->Value(af1); + + if (std::abs(aLPntOfIPC1.X() - aFPntOfPC1.X()) > Precision::Confusion()) + { diff --git a/deps/OCCT/OCCT.cmake b/deps/OCCT/OCCT.cmake index 4cab60ec6a..0466d8e868 100644 --- a/deps/OCCT/OCCT.cmake +++ b/deps/OCCT/OCCT.cmake @@ -25,9 +25,16 @@ endif() # shipped bytes. Windows ships only the DLLs libslic3r links, so the tab adds the TKFillet, # TKOffset and TKBool DLLs. See docs/HLSD/design-tab.md. +if (IN_GIT_REPO) + set(OCCT_DIRECTORY_FLAG --directory ${BINARY_DIR_REL}/dep_OCCT-prefix/src/dep_OCCT) +endif () + orcaslicer_add_cmake_project(OCCT URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V8_0_1.zip URL_HASH SHA256=7c033d917ee8f040c0512d289dcc5f02c148889d5bac17c3e25639accb44f0da + # Makes BRepMesh triangulate cone faces whose seam pcurve is slightly tilted + # (Open-Cascade-SAS/OCCT#572); remove the patch once an OCCT release includes the fix. + PATCH_COMMAND git apply ${OCCT_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-BRepMesh-seam-pcurve-at-edge-parameter.patch #DEPENDS dep_Boost DEPENDS ${FREETYPE_PKG} CMAKE_ARGS diff --git a/scripts/Dockerfile.deps-assimp b/scripts/Dockerfile.deps-assimp index 40566885c0..505eb3e4f5 100644 --- a/scripts/Dockerfile.deps-assimp +++ b/scripts/Dockerfile.deps-assimp @@ -46,7 +46,7 @@ RUN set -eux; \ tar -xzf /tmp/assimp.tar.gz -C /tmp/assimp-src --strip-components=1; \ DESTDIR=/OrcaSlicer/deps/build/destdir/usr/local; \ cmake -S /tmp/assimp-src -B /tmp/assimp-build -G Ninja \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.10 \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="$DESTDIR" \ -DCMAKE_PREFIX_PATH="$DESTDIR" \ diff --git a/src/dev-utils/platform/unix/build_linux_image.sh.in b/src/dev-utils/platform/unix/build_linux_image.sh.in index 8466597d34..03dc483b7d 100755 --- a/src/dev-utils/platform/unix/build_linux_image.sh.in +++ b/src/dev-utils/platform/unix/build_linux_image.sh.in @@ -303,10 +303,11 @@ else fi has_host_runtime_library() { - local lib_name path + local lib_name="\$1" + local path if command -v ldconfig >/dev/null 2>&1; then - if ldconfig -p 2>/dev/null | grep -Fq " \$lib_name"; then + if ldconfig -p 2>/dev/null | grep -Fq "\$lib_name ("; then return 0 fi fi diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 44d54be99d..37e3c84b3f 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -1045,7 +1045,28 @@ void do_boolean(McutMesh& srcMesh, const McutMesh& cutMesh, const std::string& b // But we can force it to work by spliting the src mesh into disconnected components, // and do booleans seperately, then merge all the results. indexed_triangle_set all_its; - if (boolean_opts == "UNION" || boolean_opts == "A_NOT_B") { + if (boolean_opts == "A_NOT_B") { + // Each cut can leave the source with several disconnected components, which mcut rejects + // in the next dispatch, so re-split after every cut part (e.g. each letter of a text). + std::vector parts = std::move(src_parts); + for (size_t j = 0; j < cut_parts.size(); j++) { + auto cut_part = triangle_mesh_to_mcut(cut_parts[j]); + std::vector next_parts; + for (indexed_triangle_set &part : parts) { + auto src_part = triangle_mesh_to_mcut(part); + if (do_boolean_single(*src_part, *cut_part, boolean_opts)) { + TriangleMesh tri_part = mcut_to_triangle_mesh(*src_part); + std::vector pieces = its_split(tri_part.its); + std::move(pieces.begin(), pieces.end(), std::back_inserter(next_parts)); + } else + next_parts.emplace_back(std::move(part)); + } + parts = std::move(next_parts); + } + for (const indexed_triangle_set &part : parts) + its_merge(all_its, part); + } + else if (boolean_opts == "UNION") { for (size_t i = 0; i < src_parts.size(); i++) { auto src_part = triangle_mesh_to_mcut(src_parts[i]); for (size_t j = 0; j < cut_parts.size(); j++) { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e2ef7c410b..ce27b0eb93 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -13241,7 +13241,7 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() // Common Defs def = this->add("layer_num", coInt); def->label = L("Layer number"); - def->tooltip = L("Index of the current layer. One-based (i.e. first layer is number 1)."); + def->tooltip = L("Index of the current layer. Zero-based (i.e. first layer is number 0), except in extrusion role change G-code, where it is one-based."); def = this->add("layer_z", coFloat); def->label = L("Layer Z"); diff --git a/tests/data/cone_tilted_seam_pcurve.step b/tests/data/cone_tilted_seam_pcurve.step new file mode 100644 index 0000000000..172012114b --- /dev/null +++ b/tests/data/cone_tilted_seam_pcurve.step @@ -0,0 +1,166 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('Open CASCADE Model'),'2;1'); +FILE_NAME('Open CASCADE Shape Model','2026-10-08T09:57:33',('Author'),( + 'Open CASCADE'),'Open CASCADE STEP processor 8.0','Open CASCADE 8.0' + ,'Unknown'); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }')); +ENDSEC; +DATA; +#1 = APPLICATION_PROTOCOL_DEFINITION('international standard', + 'automotive_design',2000,#2); +#2 = APPLICATION_CONTEXT( + 'core data for automotive mechanical design processes'); +#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10); +#4 = PRODUCT_DEFINITION_SHAPE('','',#5); +#5 = PRODUCT_DEFINITION('design','',#6,#9); +#6 = PRODUCT_DEFINITION_FORMATION('','',#7); +#7 = PRODUCT('Open CASCADE STEP translator 8.0 1', + 'Open CASCADE STEP translator 8.0 1','',(#8)); +#8 = PRODUCT_CONTEXT('',#2,'mechanical'); +#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design'); +#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#121); +#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14); +#12 = CARTESIAN_POINT('',(0.,0.,0.)); +#13 = DIRECTION('',(0.,0.,1.)); +#14 = DIRECTION('',(1.,0.,-0.)); +#15 = MANIFOLD_SOLID_BREP('',#16); +#16 = CLOSED_SHELL('',(#17,#113,#117)); +#17 = ADVANCED_FACE('',(#18),#31,.T.); +#18 = FACE_BOUND('',#19,.T.); +#19 = EDGE_LOOP('',(#20,#58,#81,#112)); +#20 = ORIENTED_EDGE('',*,*,#21,.F.); +#21 = EDGE_CURVE('',#22,#22,#24,.T.); +#22 = VERTEX_POINT('',#23); +#23 = CARTESIAN_POINT('',(-36.4,0.,25.)); +#24 = SURFACE_CURVE('',#25,(#30,#42),.PCURVE_S1.); +#25 = CIRCLE('',#26,36.4); +#26 = AXIS2_PLACEMENT_3D('',#27,#28,#29); +#27 = CARTESIAN_POINT('',(0.,0.,25.)); +#28 = DIRECTION('',(0.,0.,1.)); +#29 = DIRECTION('',(1.,0.,-0.)); +#30 = PCURVE('',#31,#36); +#31 = CONICAL_SURFACE('',#32,36.4,0.785398163397); +#32 = AXIS2_PLACEMENT_3D('',#33,#34,#35); +#33 = CARTESIAN_POINT('',(0.,0.,25.)); +#34 = DIRECTION('',(0.,0.,1.)); +#35 = DIRECTION('',(1.,0.,-0.)); +#36 = DEFINITIONAL_REPRESENTATION('',(#37),#41); +#37 = LINE('',#38,#39); +#38 = CARTESIAN_POINT('',(-6.28318530718,3.552713678801E-15)); +#39 = VECTOR('',#40,1.); +#40 = DIRECTION('',(1.,0.)); +#41 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#42 = PCURVE('',#43,#48); +#43 = PLANE('',#44); +#44 = AXIS2_PLACEMENT_3D('',#45,#46,#47); +#45 = CARTESIAN_POINT('',(0.,0.,25.)); +#46 = DIRECTION('',(-0.,-0.,-1.)); +#47 = DIRECTION('',(-1.,0.,0.)); +#48 = DEFINITIONAL_REPRESENTATION('',(#49),#57); +#49 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#50,#51,#52,#53,#54,#55,#56), +.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#50 = CARTESIAN_POINT('',(-36.4,0.)); +#51 = CARTESIAN_POINT('',(-36.4,63.046649395507)); +#52 = CARTESIAN_POINT('',(18.2,31.523324697754)); +#53 = CARTESIAN_POINT('',(72.8,8.915428697793E-15)); +#54 = CARTESIAN_POINT('',(18.2,-31.52332469775)); +#55 = CARTESIAN_POINT('',(-36.4,-63.0466493955)); +#56 = CARTESIAN_POINT('',(-36.4,0.)); +#57 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#58 = ORIENTED_EDGE('',*,*,#59,.T.); +#59 = EDGE_CURVE('',#22,#60,#62,.T.); +#60 = VERTEX_POINT('',#61); +#61 = CARTESIAN_POINT('',(-11.4,0.,3.552713678801E-15)); +#62 = SEAM_CURVE('',#63,(#67,#74),.PCURVE_S1.); +#63 = LINE('',#64,#65); +#64 = CARTESIAN_POINT('',(-36.4,0.,25.)); +#65 = VECTOR('',#66,1.); +#66 = DIRECTION('',(0.707106781187,8.659560562349E-17,-0.707106781187)); +#67 = PCURVE('',#31,#68); +#68 = DEFINITIONAL_REPRESENTATION('',(#69),#73); +#69 = LINE('',#70,#71); +#70 = CARTESIAN_POINT('',(-3.14159265359,3.552713678801E-15)); +#71 = VECTOR('',#72,1.); +#72 = DIRECTION('',(2.13162820728E-16,-1.)); +#73 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#74 = PCURVE('',#31,#75); +#75 = DEFINITIONAL_REPRESENTATION('',(#76),#80); +#76 = LINE('',#77,#78); +#77 = CARTESIAN_POINT('',(3.14159265359,3.552713678801E-15)); +#78 = VECTOR('',#79,1.); +#79 = DIRECTION('',(2.13162820728E-16,-1.)); +#80 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#81 = ORIENTED_EDGE('',*,*,#82,.F.); +#82 = EDGE_CURVE('',#60,#60,#83,.T.); +#83 = SURFACE_CURVE('',#84,(#89,#96),.PCURVE_S1.); +#84 = CIRCLE('',#85,11.4); +#85 = AXIS2_PLACEMENT_3D('',#86,#87,#88); +#86 = CARTESIAN_POINT('',(0.,0.,3.552713678801E-15)); +#87 = DIRECTION('',(0.,0.,-1.)); +#88 = DIRECTION('',(1.,0.,0.)); +#89 = PCURVE('',#31,#90); +#90 = DEFINITIONAL_REPRESENTATION('',(#91),#95); +#91 = LINE('',#92,#93); +#92 = CARTESIAN_POINT('',(6.28318530718,-25.)); +#93 = VECTOR('',#94,1.); +#94 = DIRECTION('',(-1.,0.)); +#95 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#96 = PCURVE('',#97,#102); +#97 = PLANE('',#98); +#98 = AXIS2_PLACEMENT_3D('',#99,#100,#101); +#99 = CARTESIAN_POINT('',(0.,0.,3.552713678801E-15)); +#100 = DIRECTION('',(0.,0.,1.)); +#101 = DIRECTION('',(1.,0.,-0.)); +#102 = DEFINITIONAL_REPRESENTATION('',(#103),#111); +#103 = ( BOUNDED_CURVE() B_SPLINE_CURVE(2,(#104,#105,#106,#107,#108,#109 +,#110),.UNSPECIFIED.,.T.,.F.) B_SPLINE_CURVE_WITH_KNOTS((1,2,2,2,2,1),( + -2.094395102393,0.,2.094395102393,4.188790204786,6.28318530718, +8.377580409573),.UNSPECIFIED.) CURVE() GEOMETRIC_REPRESENTATION_ITEM() +RATIONAL_B_SPLINE_CURVE((1.,0.5,1.,0.5,1.,0.5,1.)) REPRESENTATION_ITEM( + '') ); +#104 = CARTESIAN_POINT('',(11.4,0.)); +#105 = CARTESIAN_POINT('',(11.4,-19.74537920628)); +#106 = CARTESIAN_POINT('',(-5.7,-9.872689603143)); +#107 = CARTESIAN_POINT('',(-22.8,-2.792194702056E-15)); +#108 = CARTESIAN_POINT('',(-5.7,9.872689603143)); +#109 = CARTESIAN_POINT('',(11.4,19.745379206285)); +#110 = CARTESIAN_POINT('',(11.4,0.)); +#111 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) +PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' + ) ); +#112 = ORIENTED_EDGE('',*,*,#59,.F.); +#113 = ADVANCED_FACE('',(#114),#43,.F.); +#114 = FACE_BOUND('',#115,.F.); +#115 = EDGE_LOOP('',(#116)); +#116 = ORIENTED_EDGE('',*,*,#21,.F.); +#117 = ADVANCED_FACE('',(#118),#97,.F.); +#118 = FACE_BOUND('',#119,.F.); +#119 = EDGE_LOOP('',(#120)); +#120 = ORIENTED_EDGE('',*,*,#82,.F.); +#121 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) +GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#125)) GLOBAL_UNIT_ASSIGNED_CONTEXT +((#122,#123,#124)) REPRESENTATION_CONTEXT('Context #1', + '3D Context with UNIT and UNCERTAINTY') ); +#122 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) ); +#123 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) ); +#124 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() ); +#125 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-07),#122, + 'distance_accuracy_value','confusion accuracy'); +#126 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7)); +ENDSEC; +END-ISO-10303-21; diff --git a/tests/libslic3r/test_meshboolean.cpp b/tests/libslic3r/test_meshboolean.cpp index 6aebd56481..fc04ba27fc 100644 --- a/tests/libslic3r/test_meshboolean.cpp +++ b/tests/libslic3r/test_meshboolean.cpp @@ -1,9 +1,11 @@ #include #include #include - +#include +#include #include #include +#include using namespace Slic3r; @@ -24,3 +26,39 @@ TEST_CASE("CGAL and TriangleMesh conversions", "[MeshBoolean]") { REQUIRE(! MeshBoolean::cgal::does_self_intersect(M)); } + +TEST_CASE("mcut difference handles source splits between cuts", "[MeshBoolean]") { + TriangleMesh body = make_cube(30., 10., 10.); + + TriangleMesh tool; + + // First cut splits the source into two disconnected components. + TriangleMesh slab = make_cube(2., 12., 20.); + slab.translate(Vec3f(14.f, -1.f, -5.f)); + its_merge(tool.its, slab.its); + + // These cuts must still be applied after the source has been split. + TriangleMesh left_hole = make_cube(4., 4., 20.); + left_hole.translate(Vec3f(3.f, 3.f, -5.f)); + its_merge(tool.its, left_hole.its); + + TriangleMesh right_hole = make_cube(4., 4., 20.); + right_hole.translate(Vec3f(21.f, 3.f, -5.f)); + its_merge(tool.its, right_hole.its); + + std::vector result; + MeshBoolean::mcut::make_boolean(body, tool, result, "A_NOT_B"); + + REQUIRE(result.size() == 1); + + const std::vector components = + its_split(result.front().its); + + REQUIRE(components.size() == 2); + + // 3000 - 200 - 160 - 160 = 2480. + REQUIRE_THAT( + result.front().volume(), + Catch::Matchers::WithinRel(2480., 1e-3) + ); +} diff --git a/tests/libslic3r/test_step.cpp b/tests/libslic3r/test_step.cpp index 0bb83f5809..5d843499fa 100644 --- a/tests/libslic3r/test_step.cpp +++ b/tests/libslic3r/test_step.cpp @@ -8,6 +8,7 @@ #include #include "libslic3r/Model.hpp" #include "libslic3r/Format/STEP.hpp" +#include "libslic3r/TriangleMesh.hpp" #include "test_utils.hpp" using namespace Slic3r; @@ -73,6 +74,23 @@ TEST_CASE("A security classification assignment does not crash import", "[Step]" CHECK(model.objects.front()->volumes.front()->mesh().facets_count() == 4); // a tetrahedron } +// The fixture is a truncated cone whose seam pcurves have the direction (2.1e-16, -1). +TEST_CASE("A cone with a slightly tilted seam imports as a closed mesh", "[Step]") +{ + const std::string path = std::string(TEST_DATA_DIR) + PATH_SEPARATOR "cone_tilted_seam_pcurve.step"; + + Model model; + bool cancel = false; + Step step(path); + + REQUIRE(step.load() == Step::Step_Status::LOAD_SUCCESS); + REQUIRE(step.mesh(&model, cancel, false) == Step::Step_Status::MESH_SUCCESS); + + REQUIRE(model.objects.size() == 1); + REQUIRE(model.objects.front()->volumes.size() == 1); + CHECK(its_num_open_edges(model.objects.front()->volumes.front()->mesh().its) == 0); +} + TEST_CASE("isUtf8 recognises two, three and four byte sequences", "[Step]") { CHECK(StepPreProcessor::isUtf8("\xC3\xA9")); // U+00E9