diff --git a/src/csg/levelset_3d.cpp b/src/csg/levelset_3d.cpp index 3e7ce17be26ca97f27fc760b1a99626cf8f8ab17..50bad9003eb9d52afbd3ca8bdce3d29ff35f203f 100644 --- a/src/csg/levelset_3d.cpp +++ b/src/csg/levelset_3d.cpp @@ -145,10 +145,22 @@ double signed_distance_3d(const LinearExtrude &lin_ext, double xx, double yy, double signed_distance_3d(const RotateExtrude &rot_ext, double xx, double yy, double zz) { - // TODO: support angle + double tt = atan(yy / xx) * 180 / M_PI; + if ((xx < 0 && yy >= 0) || (xx < 0 && yy < 0)) + tt += 180; // Quadrants II & III + else if (xx >= 0 && yy < 0) + tt += 360; // Quadrant IV + + double sign_t = (tt >= 0 && tt <= rot_ext.angle) ? -1.0 : 1.0; + double dist_t = std::min(std::fabs(rot_ext.angle - tt), std::fabs(360 - tt)); + auto XX = std::hypot(xx, yy); auto YY = zz; - return signed_distance_2d(rot_ext.group, XX, YY); + auto sd_2d = signed_distance_2d(rot_ext.group, XX, YY); + double sign_2d = sd_2d >= 0 ? -1.0 : 1.0; + auto dist_2d = std::fabs(sd_2d); + + return EXTERNAL_FLOW * std::max(sign_t * dist_t, sign_2d * dist_2d); } double signed_distance_3d(const Polyhedron &polyhedron, double xx, double yy, diff --git a/src/csg/parser.cpp b/src/csg/parser.cpp index fc57aaba18ac780bb086adbb203da5a532ae5342..53352b41e38f1e215253f1a9ea96df176d7c728b 100644 --- a/src/csg/parser.cpp +++ b/src/csg/parser.cpp @@ -468,6 +468,12 @@ template <> struct action { auto &curr_attr = st.curr_attrs.back(); rot_ext.angle = std::get(curr_attr["angle"]); + if (rot_ext.angle < 0 || rot_ext.angle > 360) { + throw csg::Exception("action", + "angle=" + std::to_string(rot_ext.angle) + + "; must be between 0 and 360!"); + } + for (const auto &curr_obj : st.current_2d_group) { rot_ext.group.objs.push_back(curr_obj); } diff --git a/src/csg/tests/levelset/extrude.t.cpp b/src/csg/tests/levelset/extrude.t.cpp index e8d7e1b1d05e3e414501bdd85e9d1c38aaa1fc12..a9b15b0509a7b85ba8d6f7cc8242040d989c17fe 100644 --- a/src/csg/tests/levelset/extrude.t.cpp +++ b/src/csg/tests/levelset/extrude.t.cpp @@ -133,7 +133,7 @@ TEST_CASE("two shape linear extrude", "[Levelset Extrude]") { } TEST_CASE("simple torus", "[Levelset Extrude]") { - auto my_rot_ext = csg::RotateExtrude(); + auto my_rot_ext = csg::RotateExtrude{.angle = 360, .group = csg::Union2D()}; csg::Circle my_cir{.name = std::nullopt, .radius = 1}; auto my_mat = csg::Mulmatrix2D({{1, 0}}, {{0, 1}}); @@ -245,4 +245,38 @@ TEST_CASE("Linear extrude of a triangle with hole", "[Levelset Primitives]") { } } +TEST_CASE("sliced torus", "[Levelset Extrude]") { + auto my_rot_ext = csg::RotateExtrude{.angle = 45, .group = csg::Union2D()}; + + csg::Circle my_cir{.name = std::nullopt, .radius = 1}; + auto my_mat = csg::Mulmatrix2D({{1, 0}}, {{0, 1}}); + my_mat.translation = {2, 0}; + my_mat.group.objs.push_back(my_cir); + my_rot_ext.group.objs.push_back(my_mat); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_rot_ext); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(0, 0, 0)); + CHECK_FALSE(0 < my_levelset(0.9, 0, 0)); + CHECK_FALSE(0 < my_levelset(-0.9, 0, 0)); + CHECK_FALSE(0 < my_levelset(2, 0, 1.1)); + CHECK_FALSE(0 < my_levelset(-2, 0, -1.1)); + CHECK_FALSE(0 < my_levelset(2, -0.01, 0)); + CHECK_FALSE(0 < my_levelset(1.5, 1.6, 0.9)); + CHECK_FALSE(0 < my_levelset(-1.1, 0.0, 0)); + CHECK_FALSE(0 < my_levelset(1.5, 1.6, -0.5)); + CHECK_FALSE(0 < my_levelset(-2, 0.0, -0.9)); + } + SECTION("Inside") { + CHECK(0 < my_levelset(2, 0.01, 0)); + CHECK(0 < my_levelset(1.1, 0.01, 0)); + CHECK(0 < my_levelset(2, 0.01, 0.9)); + CHECK(0 < my_levelset(1.5, 1.4, 0.9)); + CHECK(0 < my_levelset(1.5, 1.4, -0.5)); + } +} + } // namespace diff --git a/src/csg/tests/parser/extrude.t.cpp b/src/csg/tests/parser/extrude.t.cpp index 3a2b5640bf10cd6bfeb11c0fc3f92295ff1170c2..749e3c10aa93f54281a94dd62cca4517b7ee4ee0 100644 --- a/src/csg/tests/parser/extrude.t.cpp +++ b/src/csg/tests/parser/extrude.t.cpp @@ -46,6 +46,17 @@ angle = 90 CHECK(yy == 3); } +TEST_CASE("rotate extrude with bad angle", "[csg]") { + CHECK_THROWS_WITH(csg::parse_csg(R"( +rotate_extrude( +angle = 370 +) { + square(size = [2, 3], center = false); +} +)"), + Catch::Contains("action")); +} + TEST_CASE("extrude two shapes", "[csg]") { auto st = *csg::parse_csg(R"( linear_extrude(