diff --git a/src/csg/levelset_2d.cpp b/src/csg/levelset_2d.cpp index cdfb92caaaad879615cf57a5cc7363b0205f7951..66a528425e674d55fec65cb5502ee3b8808dda40 100644 --- a/src/csg/levelset_2d.cpp +++ b/src/csg/levelset_2d.cpp @@ -47,6 +47,9 @@ double signed_distance_2d(const Intersection2D &group, double xx, double yy) { } double signed_distance_2d(const Difference2D &group, double xx, double yy) { + if (group.first_obj == nullptr) + return -std::numeric_limits::max(); + auto sdist = signed_distance_2d(*group.first_obj, xx, yy); for (const auto &member : group.next_objs.objs) { diff --git a/src/csg/levelset_3d.cpp b/src/csg/levelset_3d.cpp index 75343ddec72a806210037703f5a895afb1c88b29..00e8b571b625ff7c3ae106743c9314111c1e40bc 100644 --- a/src/csg/levelset_3d.cpp +++ b/src/csg/levelset_3d.cpp @@ -57,6 +57,9 @@ double signed_distance_3d(const Intersection3D &group, double xx, double yy, double signed_distance_3d(const Difference3D &group, double xx, double yy, double zz) { + if (group.first_obj == nullptr) + return -std::numeric_limits::max(); + auto sdist = signed_distance_3d(*group.first_obj, xx, yy, zz); for (const auto &member : group.next_objs.objs) { diff --git a/src/csg/parser.cpp b/src/csg/parser.cpp index 10d229b0bfa09f57abf0563a0731b60391bae8ab..961d079add22cd4b7032e95dd480e4d94c612ee4 100644 --- a/src/csg/parser.cpp +++ b/src/csg/parser.cpp @@ -292,6 +292,7 @@ void add_group_2d(parser_state &st) { for (const auto &curr_obj : st.current_2d_group) { group.objs.push_back(curr_obj); } + st.current_2d_group.clear(); st.current_2d_objs.back().push_back(group); } @@ -300,6 +301,7 @@ void add_group_3d(parser_state &st) { for (const auto &curr_obj : st.current_3d_group) { group.objs.push_back(curr_obj); } + st.current_3d_group.clear(); st.current_3d_objs.back().push_back(group); } @@ -359,6 +361,7 @@ template <> struct action> { for (const auto &curr_obj : st.current_3d_group) { csg_in.objs.push_back(curr_obj); } + st.current_3d_group.clear(); st.current_3d_objs.back().push_back(csg_in); } }; @@ -371,6 +374,7 @@ template <> struct action> { for (const auto &curr_obj : st.current_2d_group) { csg_in.objs.push_back(curr_obj); } + st.current_2d_group.clear(); st.current_2d_objs.back().push_back(csg_in); } }; @@ -388,6 +392,7 @@ template <> struct action> { csg_diff.next_objs.objs.push_back(*it); } } + st.current_3d_group.clear(); st.current_3d_objs.back().push_back(csg_diff); } }; @@ -405,6 +410,7 @@ template <> struct action> { csg_diff.next_objs.objs.push_back(*it); } } + st.current_2d_group.clear(); st.current_2d_objs.back().push_back(csg_diff); } }; @@ -470,6 +476,7 @@ template <> struct action { lin_ext.group.objs.push_back(curr_obj); } + st.current_2d_group.clear(); st.current_3d_objs.back().push_back(lin_ext); st.curr_attrs.pop_back(); } @@ -493,6 +500,7 @@ template <> struct action { rot_ext.group.objs.push_back(curr_obj); } + st.current_3d_group.clear(); st.current_3d_objs.back().push_back(rot_ext); st.curr_attrs.pop_back(); } @@ -525,6 +533,7 @@ template <> struct action { } } } + st.current_3d_group.clear(); // If the conditions were not met and throw exception std::string except_src = "action"; diff --git a/src/csg/tests/CMakeLists.txt b/src/csg/tests/CMakeLists.txt index 7450fc8d4fe82813bb1d381ef8270e4ff0f90e61..c4d13e05bb5f9036cb8b81e5bad7ab8a8d3d1d07 100644 --- a/src/csg/tests/CMakeLists.txt +++ b/src/csg/tests/CMakeLists.txt @@ -4,12 +4,14 @@ add_executable(unit_tests_csg EXCLUDE_FROM_ALL levelset/boolean.t.cpp + levelset/empty.t.cpp levelset/extrude.t.cpp levelset/hull.t.cpp levelset/internal_flow.t.cpp levelset/primitives.t.cpp levelset/transform.t.cpp parser/boolean.t.cpp + parser/empty.t.cpp parser/extrude.t.cpp parser/hull.t.cpp parser/nest.cpp diff --git a/src/csg/tests/levelset/empty.t.cpp b/src/csg/tests/levelset/empty.t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef29f4fc7b1050e23d9772b6e27a15e899644a15 --- /dev/null +++ b/src/csg/tests/levelset/empty.t.cpp @@ -0,0 +1,45 @@ +#include "catch2/catch.hpp" + +#include +#include + +#include + +namespace { + +TEST_CASE("Empty Difference3D", "[Levelset Boolean]") { + auto my_diff = csg::Difference3D(); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_diff); + csg::CsgIF my_levelset(my_tree); + + // Any point should lie outside! + CHECK_FALSE(0 < my_levelset(0, 0, 0)); + CHECK_FALSE(0 < my_levelset(0, 7, 0)); + CHECK_FALSE(0 < my_levelset(0, 0, -7.5)); + CHECK_FALSE(0 < my_levelset(0, -9, 0)); + CHECK_FALSE(0 < my_levelset(9, 0, 0)); +} + +TEST_CASE("Empty Difference2D", "[Levelset Boolean]") { + double height = 100, radius = 10; + auto my_lin_ext = csg::LinearExtrude{ + .height = 100, .center = false, .scale = {1, 1}, .group = csg::Union2D()}; + auto my_diff = csg::Difference2D(); + + my_lin_ext.center = false; + my_lin_ext.group.objs.push_back(my_diff); + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_lin_ext); + csg::CsgIF my_levelset(my_tree); + + // Any point should lie outside! + CHECK_FALSE(0 < my_levelset(0, 0, 0)); + CHECK_FALSE(0 < my_levelset(0, 7, 0)); + CHECK_FALSE(0 < my_levelset(0, 0, -7.5)); + CHECK_FALSE(0 < my_levelset(0, -9, 0)); + CHECK_FALSE(0 < my_levelset(9, 0, 0)); +} + +} diff --git a/src/csg/tests/parser/boolean.t.cpp b/src/csg/tests/parser/boolean.t.cpp index 378de7671128be2d9573a359954e2b2321112e39..8a563511a876d204aaa477c578d00cf0fea4201b 100644 --- a/src/csg/tests/parser/boolean.t.cpp +++ b/src/csg/tests/parser/boolean.t.cpp @@ -119,50 +119,3 @@ multmatrix([[1, 0, 0, 0.5], [0, 1, 0, 0.1], [0, 0, 1, 0], [0, 0, 0, 1]]) { CHECK(cone.radius2 == 0.4); CHECK(cone.center == false); } - -TEST_CASE("mulmat and empty group", "[csg]") { - auto st = *csg::parse_csg(R"( -multmatrix([[0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1]]) { - cylinder($fn = 0, $fa = 12, $fs = 2, h = 200, r1 = 7, r2 = 7, center = false); -} -group(); -)"); - auto un = std::get(st.top.objs.at(1)); - - CHECK(un.objs.size() == 0); -} - -TEST_CASE("empty boolean", "[csg]") { - auto st = *csg::parse_csg(R"( -union(); -sphere(r=0.1); -)"); - auto un = std::get(st.top.objs.at(0)); - CHECK(un.objs.size() == 0); - - auto sph = std::get(st.top.objs.at(1)); - CHECK(sph.radius == 0.1); -} - -TEST_CASE("empty intersection", "[csg]") { - auto st = *csg::parse_csg(R"( -intersection(); -)"); - auto in = std::get(st.top.objs.at(0)); - CHECK(in.objs.size() == 0); -} - -TEST_CASE("empty difference", "[csg]") { - auto st = *csg::parse_csg(R"( -cylinder(h = 200, r1 = 1.7, r2 = 2.7, center = false); -difference(); -)"); - auto cone = std::get(st.top.objs.at(0)); - CHECK(cone.radius1 == 1.7); - CHECK(cone.radius2 == 2.7); - CHECK(cone.height == 200); - - auto diff = std::get(st.top.objs.at(1)); - CHECK(diff.first_obj == nullptr); - CHECK(diff.next_objs.objs.size() == 0); -} diff --git a/src/csg/tests/parser/empty.t.cpp b/src/csg/tests/parser/empty.t.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60a3b60cbc4d89102e58e9867bb57313c0d8f933 --- /dev/null +++ b/src/csg/tests/parser/empty.t.cpp @@ -0,0 +1,171 @@ +#include "catch2/catch.hpp" + +#include +#include + +// Tests for CSG union(), intersection(), and difference() + +namespace { +TEST_CASE("mulmat and empty group", "[csg]") { + auto st = *csg::parse_csg(R"( +multmatrix([[0, 0, 1, 0], [0, 1, 0, 0], [-1, 0, 0, 0], [0, 0, 0, 1]]) { + cylinder($fn = 0, $fa = 12, $fs = 2, h = 200, r1 = 7, r2 = 7, center = false); +} +group(); +)"); + auto un = std::get(st.top.objs.at(1)); + + CHECK(un.objs.size() == 0); +} + +TEST_CASE("difference and empty group", "[csg]") { + auto st = *csg::parse_csg(R"( +difference() { + sphere(r = 2); + sphere(r = 1); +} +group(); +)"); + auto diff = std::get(st.top.objs.at(0)); + auto sph1 = std::get(*diff.first_obj); + auto sph2 = std::get(diff.next_objs.objs.at(0)); + + auto un = std::get(st.top.objs.at(1)); + + CHECK(sph1.radius == 2); + CHECK(sph2.radius == 1); + CHECK(un.objs.size() == 0); +} + +TEST_CASE("intersection and empty group", "[csg]") { + auto st = *csg::parse_csg(R"( +intersection() { + sphere(r = 2); + sphere(r = 3); +} +group(); +)"); + auto un = std::get(st.top.objs.at(1)); + + CHECK(un.objs.size() == 0); +} + +TEST_CASE("extrude and empty group", "[csg]") { + auto st = *csg::parse_csg(R"( +linear_extrude(height = 10, center = false, scale = [5, 5]) { + circle(r = 5); +} +group(); +)"); + auto un = std::get(st.top.objs.at(1)); + + CHECK(un.objs.size() == 0); +} + +TEST_CASE("empty boolean", "[csg]") { + auto st = *csg::parse_csg(R"( +union(); +sphere(r=0.1); +)"); + auto un = std::get(st.top.objs.at(0)); + CHECK(un.objs.size() == 0); + + auto sph = std::get(st.top.objs.at(1)); + CHECK(sph.radius == 0.1); +} + +TEST_CASE("empty intersection", "[csg]") { + auto st = *csg::parse_csg(R"( +intersection(); +)"); + auto in = std::get(st.top.objs.at(0)); + CHECK(in.objs.size() == 0); +} + +TEST_CASE("empty difference", "[csg]") { + auto st = *csg::parse_csg(R"( +cylinder(h = 200, r1 = 1.7, r2 = 2.7, center = false); +difference(); +)"); + auto cone = std::get(st.top.objs.at(0)); + CHECK(cone.radius1 == 1.7); + CHECK(cone.radius2 == 2.7); + CHECK(cone.height == 200); + + auto diff = std::get(st.top.objs.at(1)); + CHECK(diff.first_obj == nullptr); + CHECK(diff.next_objs.objs.size() == 0); +} + +TEST_CASE("empty matmul", "[csg]") { + auto st = *csg::parse_csg(R"( +multmatrix( +[ +[1, 0, 0, 0.0020], +[0, 1, 0, 0.0005], +[0, 0, 1, 0.0005], +[0, 0, 0, 1] +] +); +)"); + auto mat = std::get(st.top.objs.back()); + CHECK(mat.group.objs.size() == 0); + CHECK(mat.translation == std::array({0.0020, 0.0005, 0.0005})); + CHECK(mat.rotation()[0] == std::array({1, 0, 0})); + CHECK(mat.rotation()[1] == std::array({0, 1, 0})); + CHECK(mat.rotation()[2] == std::array({0, 0, 1})); +} + +TEST_CASE("empty intersection inside linear extrude", "[csg]") { + auto st = *csg::parse_csg(R"( +linear_extrude( +height = 10, +center = true, +scale = [10,1] +) { + circle(r=0.2); + intersection(); +} +)"); + auto lin_ext = std::get(st.top.objs.at(0)); + CHECK(lin_ext.group.objs.size() == 2); + + auto cir = std::get(lin_ext.group.objs.at(0)); + CHECK(cir.radius == 0.2); + + auto in = std::get(lin_ext.group.objs.at(1)); + CHECK(in.objs.size() == 0); +} + +TEST_CASE("empty linear_extrude", "[csg]") { + auto st = *csg::parse_csg(R"( +linear_extrude(height = 10, center = true, scale = [10,1]); +)"); + auto lin_ext = std::get(st.top.objs.at(0)); + CHECK(lin_ext.height == 10); + CHECK(lin_ext.center == true); + + auto [sx, sy] = lin_ext.scale; + CHECK(sx == 10); + CHECK(sy == 1); +} + +TEST_CASE("group and empty multmatrix", "[csg]") { + auto st = *csg::parse_csg(R"( +group() { + multmatrix([[1, 0, 0, 0.2], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cylinder($fn = 50, $fa = 12, $fs = 2, h = 0.636, r1 = 0.05, r2 = 0.05, center = false); + } +} +multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.636], [0, 0, 0, 1]]); +)"); + auto gp = std::get(st.top.objs.at(0)); + auto mm1 = std::get(gp.objs.at(0)); + auto cone = std::get(mm1.group.objs.at(0)); + CHECK(cone.height == 0.636); + + auto mm2 = std::get(st.top.objs.at(1)); + CHECK(mm2.group.objs.size() == 0); +} + +} // namespace diff --git a/src/csg/tests/parser/extrude.t.cpp b/src/csg/tests/parser/extrude.t.cpp index 6a89ff1fb94baec42c25804725f47a3f8b2a0969..749e3c10aa93f54281a94dd62cca4517b7ee4ee0 100644 --- a/src/csg/tests/parser/extrude.t.cpp +++ b/src/csg/tests/parser/extrude.t.cpp @@ -189,38 +189,4 @@ paths = [[0, 1, 2], [3, 4, 5]]); CHECK(inner.cgal_polygon().size() == 3); } -TEST_CASE("empty linear_extrude", "[csg]") { - auto st = *csg::parse_csg(R"( -linear_extrude(height = 10, center = true, scale = [10,1]); -)"); - auto lin_ext = std::get(st.top.objs.at(0)); - CHECK(lin_ext.height == 10); - CHECK(lin_ext.center == true); - - auto [sx, sy] = lin_ext.scale; - CHECK(sx == 10); - CHECK(sy == 1); -} - -TEST_CASE("empty intersection inside linear extrude", "[csg]") { - auto st = *csg::parse_csg(R"( -linear_extrude( -height = 10, -center = true, -scale = [10,1] -) { - circle(r=0.2); - intersection(); -} -)"); - auto lin_ext = std::get(st.top.objs.at(0)); - CHECK(lin_ext.group.objs.size() == 2); - - auto cir = std::get(lin_ext.group.objs.at(0)); - CHECK(cir.radius == 0.2); - - auto in = std::get(lin_ext.group.objs.at(1)); - CHECK(in.objs.size() == 0); -} - } // namespace diff --git a/src/csg/tests/parser/transform.t.cpp b/src/csg/tests/parser/transform.t.cpp index 4b3a0527997fa23731b986a042bdba8133380843..e7fa6bca7d43b9070d4255f38b23323f57c1c378 100644 --- a/src/csg/tests/parser/transform.t.cpp +++ b/src/csg/tests/parser/transform.t.cpp @@ -151,22 +151,3 @@ multmatrix( CHECK(cone.radius1 == 1); CHECK(cone.radius2 == 2); } - -TEST_CASE("empty matmul", "[csg]") { - auto st = *csg::parse_csg(R"( -multmatrix( -[ -[1, 0, 0, 0.0020], -[0, 1, 0, 0.0005], -[0, 0, 1, 0.0005], -[0, 0, 0, 1] -] -); -)"); - auto mat = std::get(st.top.objs.back()); - CHECK(mat.group.objs.size() == 0); - CHECK(mat.translation == std::array({0.0020, 0.0005, 0.0005})); - CHECK(mat.rotation()[0] == std::array({1, 0, 0})); - CHECK(mat.rotation()[1] == std::array({0, 1, 0})); - CHECK(mat.rotation()[2] == std::array({0, 0, 1})); -}