Loading include/csg_types.hpp +2 −1 Original line number Diff line number Diff line Loading @@ -186,6 +186,7 @@ struct LinearExtrude { double height; bool center; std::tuple<double, double> scale; std::optional<double> twist; Union<Dimension::D2> group; }; Loading src/csg/levelset_3d.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -137,11 +137,20 @@ double signed_distance_3d(const Cylinder &cyl, double xx, double yy, double signed_distance_3d(const LinearExtrude &lin_ext, double xx, double yy, double zz) { // TODO: Support scale double XX = xx; double YY = yy; double ZZ = lin_ext.center ? zz + lin_ext.height / 2 : zz; double sign_z = (ZZ >= 0 && ZZ <= lin_ext.height) ? -1.0 : 1.0; auto dist_z = std::min(std::fabs(ZZ), std::fabs(ZZ - lin_ext.height)); auto sd_2d = signed_distance_2d(lin_ext.group, xx, yy); if (lin_ext.twist.has_value()) { double tt = (lin_ext.twist.value() * ZZ / lin_ext.height) / DEGREES_PER_RADIAN; XX = std::hypot(xx, yy) * std::cos(std::atan2(yy, xx) - tt); YY = std::hypot(xx, yy) * std::sin(std::atan2(yy, xx) - tt); } auto sd_2d = signed_distance_2d(lin_ext.group, XX, YY); double sign_2d = sd_2d >= 0 ? -1.0 : 1.0; auto dist_2d = std::fabs(sd_2d); Loading src/csg/parser.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -472,6 +472,11 @@ template <> struct action<extrude_lin> { auto scale = std::get<std::vector<double>>(curr_attr["scale"]); lin_ext.scale = {scale[0], scale[1]}; lin_ext.twist = std::nullopt; if (curr_attr.count("twist") != 0) { lin_ext.twist = std::get<double>(curr_attr["twist"]); } for (const auto &curr_obj : st.current_2d_group) { lin_ext.group.objs.push_back(curr_obj); } Loading src/csg/tests/levelset/extrude.t.cpp +48 −0 Original line number Diff line number Diff line Loading @@ -279,4 +279,52 @@ TEST_CASE("sliced torus", "[Levelset Extrude]") { } } TEST_CASE("linear with twist", "[Levelset Extrude]") { double height = 10, radius = 1; auto my_lin_ext = csg::LinearExtrude{.height = height, .center = false, .scale = {1, 1}, .twist = 320, .group = csg::Union2D()}; csg::Circle my_cir{.name = std::nullopt, .radius = radius}; auto my_mat = csg::Mulmatrix2D({{1, 0}}, {{0, 1}}); my_mat.translation = {2, 0}; my_mat.group.objs.push_back(my_cir); my_lin_ext.group.objs.push_back(my_mat); SECTION("Not Centered") { my_lin_ext.center = false; auto my_tree = std::make_shared<csg::Tree>(); my_tree->top.objs.push_back(my_lin_ext); csg::CsgIF my_levelset(my_tree); SECTION("Outside") { CHECK_FALSE(0 < my_levelset(-1, 0, height / 2)); CHECK_FALSE(0 < my_levelset(-2.9, 0, height / 2)); } SECTION("Inside") { CHECK(0 < my_levelset(-1.3, 0, height / 2)); CHECK_FALSE(0 < my_levelset(-2.7, 0, height / 2)); } } SECTION("Centered") { my_lin_ext.center = true; auto my_tree = std::make_shared<csg::Tree>(); my_tree->top.objs.push_back(my_lin_ext); csg::CsgIF my_levelset(my_tree); SECTION("Outside") { CHECK_FALSE(0 < my_levelset(-1, 0, 0)); CHECK_FALSE(0 < my_levelset(-2.9, 0, 0)); } SECTION("Inside") { CHECK(0 < my_levelset(-1.3, 0, 0)); CHECK_FALSE(0 < my_levelset(-2.7, 0, 0)); } } } } // namespace src/csg/tests/parser/extrude.t.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,32 @@ scale = [10,1] CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 10); CHECK(Sy == 1); auto cir = std::get<csg::Circle>(lin_ext.group.objs.at(0)); CHECK(cir.radius == 1); } TEST_CASE("linear extrude circle with twist", "[csg]") { auto st = *csg::parse_csg(R"( linear_extrude( height = 10, center = true, scale = [10,1], twist = -100 ) { circle(r = 1); } )"); auto lin_ext = std::get<csg::LinearExtrude>(st.top.objs.back()); CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == true); CHECK(lin_ext.twist == -100); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 10); Loading Loading @@ -73,6 +99,7 @@ scale = [5, 3] CHECK(lin_ext.group.objs.size() == 2); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 5); Loading Loading @@ -101,6 +128,7 @@ linear_extrude(height = 10, center = false, scale = [5, 3]) { CHECK(lin_ext.group.objs.size() == 2); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == false); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 5); Loading Loading @@ -149,6 +177,7 @@ paths = undef); CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 100); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 1); Loading Loading @@ -177,6 +206,7 @@ paths = [[0, 1, 2], [3, 4, 5]]); CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 100); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 1); Loading Loading
include/csg_types.hpp +2 −1 Original line number Diff line number Diff line Loading @@ -186,6 +186,7 @@ struct LinearExtrude { double height; bool center; std::tuple<double, double> scale; std::optional<double> twist; Union<Dimension::D2> group; }; Loading
src/csg/levelset_3d.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -137,11 +137,20 @@ double signed_distance_3d(const Cylinder &cyl, double xx, double yy, double signed_distance_3d(const LinearExtrude &lin_ext, double xx, double yy, double zz) { // TODO: Support scale double XX = xx; double YY = yy; double ZZ = lin_ext.center ? zz + lin_ext.height / 2 : zz; double sign_z = (ZZ >= 0 && ZZ <= lin_ext.height) ? -1.0 : 1.0; auto dist_z = std::min(std::fabs(ZZ), std::fabs(ZZ - lin_ext.height)); auto sd_2d = signed_distance_2d(lin_ext.group, xx, yy); if (lin_ext.twist.has_value()) { double tt = (lin_ext.twist.value() * ZZ / lin_ext.height) / DEGREES_PER_RADIAN; XX = std::hypot(xx, yy) * std::cos(std::atan2(yy, xx) - tt); YY = std::hypot(xx, yy) * std::sin(std::atan2(yy, xx) - tt); } auto sd_2d = signed_distance_2d(lin_ext.group, XX, YY); double sign_2d = sd_2d >= 0 ? -1.0 : 1.0; auto dist_2d = std::fabs(sd_2d); Loading
src/csg/parser.cpp +5 −0 Original line number Diff line number Diff line Loading @@ -472,6 +472,11 @@ template <> struct action<extrude_lin> { auto scale = std::get<std::vector<double>>(curr_attr["scale"]); lin_ext.scale = {scale[0], scale[1]}; lin_ext.twist = std::nullopt; if (curr_attr.count("twist") != 0) { lin_ext.twist = std::get<double>(curr_attr["twist"]); } for (const auto &curr_obj : st.current_2d_group) { lin_ext.group.objs.push_back(curr_obj); } Loading
src/csg/tests/levelset/extrude.t.cpp +48 −0 Original line number Diff line number Diff line Loading @@ -279,4 +279,52 @@ TEST_CASE("sliced torus", "[Levelset Extrude]") { } } TEST_CASE("linear with twist", "[Levelset Extrude]") { double height = 10, radius = 1; auto my_lin_ext = csg::LinearExtrude{.height = height, .center = false, .scale = {1, 1}, .twist = 320, .group = csg::Union2D()}; csg::Circle my_cir{.name = std::nullopt, .radius = radius}; auto my_mat = csg::Mulmatrix2D({{1, 0}}, {{0, 1}}); my_mat.translation = {2, 0}; my_mat.group.objs.push_back(my_cir); my_lin_ext.group.objs.push_back(my_mat); SECTION("Not Centered") { my_lin_ext.center = false; auto my_tree = std::make_shared<csg::Tree>(); my_tree->top.objs.push_back(my_lin_ext); csg::CsgIF my_levelset(my_tree); SECTION("Outside") { CHECK_FALSE(0 < my_levelset(-1, 0, height / 2)); CHECK_FALSE(0 < my_levelset(-2.9, 0, height / 2)); } SECTION("Inside") { CHECK(0 < my_levelset(-1.3, 0, height / 2)); CHECK_FALSE(0 < my_levelset(-2.7, 0, height / 2)); } } SECTION("Centered") { my_lin_ext.center = true; auto my_tree = std::make_shared<csg::Tree>(); my_tree->top.objs.push_back(my_lin_ext); csg::CsgIF my_levelset(my_tree); SECTION("Outside") { CHECK_FALSE(0 < my_levelset(-1, 0, 0)); CHECK_FALSE(0 < my_levelset(-2.9, 0, 0)); } SECTION("Inside") { CHECK(0 < my_levelset(-1.3, 0, 0)); CHECK_FALSE(0 < my_levelset(-2.7, 0, 0)); } } } } // namespace
src/csg/tests/parser/extrude.t.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,32 @@ scale = [10,1] CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 10); CHECK(Sy == 1); auto cir = std::get<csg::Circle>(lin_ext.group.objs.at(0)); CHECK(cir.radius == 1); } TEST_CASE("linear extrude circle with twist", "[csg]") { auto st = *csg::parse_csg(R"( linear_extrude( height = 10, center = true, scale = [10,1], twist = -100 ) { circle(r = 1); } )"); auto lin_ext = std::get<csg::LinearExtrude>(st.top.objs.back()); CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == true); CHECK(lin_ext.twist == -100); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 10); Loading Loading @@ -73,6 +99,7 @@ scale = [5, 3] CHECK(lin_ext.group.objs.size() == 2); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 5); Loading Loading @@ -101,6 +128,7 @@ linear_extrude(height = 10, center = false, scale = [5, 3]) { CHECK(lin_ext.group.objs.size() == 2); CHECK(lin_ext.height == 10); CHECK(lin_ext.center == false); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 5); Loading Loading @@ -149,6 +177,7 @@ paths = undef); CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 100); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 1); Loading Loading @@ -177,6 +206,7 @@ paths = [[0, 1, 2], [3, 4, 5]]); CHECK(lin_ext.group.objs.size() == 1); CHECK(lin_ext.height == 100); CHECK(lin_ext.center == true); CHECK(!lin_ext.twist.has_value()); auto [Sx, Sy] = lin_ext.scale; CHECK(Sx == 1); Loading