diff --git a/include/csg_types.hpp b/include/csg_types.hpp index 0ffe415c64b41b7e889c752a4b63fa6e59f2c186..7004c81adb02d94f58a12b4d86df8312648c01cd 100644 --- a/include/csg_types.hpp +++ b/include/csg_types.hpp @@ -75,6 +75,29 @@ public: } }; +struct Polygon { +private: + std::vector> m_points; + std::vector m_path; + cgal_helper::Polygon m_cgal_polygon; + +public: + std::optional name; + Polygon(const std::vector> &points, + const std::vector &path) { + for (const auto &pt : points) { + assert(pt.size() == 2); + m_points.push_back({pt[0], pt[1]}); + } + for (const auto &p : path) { + m_path.push_back((unsigned int)(p)); + } + m_cgal_polygon = cgal_helper::create_polygon(m_points, m_path); + } + + const cgal_helper::Polygon &cgal_polygon() const { return m_cgal_polygon; } +}; + enum Dimension { D2, D3 }; template struct Mulmatrix; @@ -90,7 +113,7 @@ template <> struct TypeHelper { using Type = std::variant, Intersection, Difference, - Mulmatrix>; + Mulmatrix, Polygon>; }; template <> struct TypeHelper { diff --git a/src/csg/CMakeLists.txt b/src/csg/CMakeLists.txt index 7a0dd93cf023c8a69eaf050c926bf6c5c81a884c..2855e95aacabc0979d11c13a54309b1ff5d88137 100644 --- a/src/csg/CMakeLists.txt +++ b/src/csg/CMakeLists.txt @@ -7,7 +7,8 @@ add_library(csg levelset_3d.cpp matrix_functions.cpp parser.cpp - cgal_helper.cpp + cgal_helper_polyhedron.cpp + cgal_helper_polygon.cpp ) target_link_libraries(csg PRIVATE diff --git a/src/csg/cgal_helper_polygon.cpp b/src/csg/cgal_helper_polygon.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b95b9d9b977d3206bc4e0c26f48d04aca25a0fc --- /dev/null +++ b/src/csg/cgal_helper_polygon.cpp @@ -0,0 +1,39 @@ +#include "csg_cgal_helper.hpp" + +#include +#include + +namespace { +typedef CGAL::Polygon_2 Polygon; +typedef CGAL::Point_2 Point; +} // namespace + +namespace cgal_helper { + +Polygon create_polygon(const std::vector> &points, + const std::vector &path) { + + Polygon polyg; + + if (path.empty()) { // Include all points + for (const auto &p : points) { + auto [px, py] = p; + polyg.push_back(Point(px, py)); + } + } else { + for (auto p_index : path) { + auto [px, py] = points[p_index]; + polyg.push_back(Point(px, py)); + } + } + + return polyg; +} + +bool inside(const Polygon &polygon, double xx, double yy) { + + return CGAL::bounded_side_2(polygon.begin(), polygon.end(), Point(xx, yy), + IK()) == CGAL::ON_BOUNDED_SIDE; +} + +} // namespace cgal_helper diff --git a/src/csg/cgal_helper.cpp b/src/csg/cgal_helper_polyhedron.cpp similarity index 87% rename from src/csg/cgal_helper.cpp rename to src/csg/cgal_helper_polyhedron.cpp index 72378cc3869651a1a5a28844c4e45241a4706c62..ad43e7f77aed26c1ea0633cecca8dca7a074684f 100644 --- a/src/csg/cgal_helper.cpp +++ b/src/csg/cgal_helper_polyhedron.cpp @@ -9,17 +9,17 @@ #include #include -namespace cgal_helper { - namespace { -typedef Polyhedron::HalfedgeDS HalfedgeDS; +typedef cgal_helper::Polyhedron::HalfedgeDS HalfedgeDS; typedef typename HalfedgeDS::Vertex Vertex; typedef typename Vertex::Point Point; -typedef CGAL::AABB_face_graph_triangle_primitive Primitive; -typedef CGAL::AABB_traits Traits; +typedef CGAL::AABB_face_graph_triangle_primitive + Primitive; +typedef CGAL::AABB_traits Traits; typedef CGAL::AABB_tree Tree; -typedef CGAL::Side_of_triangle_mesh Point_inside; +typedef CGAL::Side_of_triangle_mesh + Point_inside; // A modifier creating a polyhedron with the incremental builder. template class PolyhedronBuilder : public CGAL::Modifier_base { @@ -58,6 +58,8 @@ public: } // namespace +namespace cgal_helper { + Polyhedron create_polyhedron(const std::vector> &points, const std::vector> &faces) { @@ -76,7 +78,7 @@ create_polyhedron(const std::vector> &points, } bool inside(const Polyhedron &polyhedron, double xx, double yy, double zz) { - Kernel::Point_3 pt(xx, yy, zz); + cgal_helper::CK::Point_3 pt(xx, yy, zz); // Construct AABB tree with a KdTree Tree tree(faces(polyhedron).first, faces(polyhedron).second, polyhedron); tree.accelerate_distance_queries(); diff --git a/src/csg/levelset_2d.cpp b/src/csg/levelset_2d.cpp index 3eff4182f47d7b40e8a62fee470264a365895aa9..b8e0993d7fdd5c9e47e00b9adb943c16553ec1eb 100644 --- a/src/csg/levelset_2d.cpp +++ b/src/csg/levelset_2d.cpp @@ -1,3 +1,4 @@ +#include "csg_cgal_helper.hpp" #include "csg_types.hpp" #include @@ -24,6 +25,7 @@ double signed_distance_2d(const Difference2D &, double, double); double signed_distance_2d(const Intersection2D &, double, double); double signed_distance_2d(const Mulmatrix2D &, double, double); double signed_distance_2d(const Union2D &, double, double); +double signed_distance_2d(const Polygon &, double, double); double signed_distance_2d(const Union2D &group, double xx, double yy) { auto sdist = -std::numeric_limits::max(); @@ -85,6 +87,12 @@ double signed_distance_2d(const Circle &cir, double xx, double yy) { return EXTERNAL_FLOW * sign * dist; } +double signed_distance_2d(const Polygon &polygon, double xx, double yy) { + + // TODO: support signed distance instead of -1.0/1.0 + return cgal_helper::inside(polygon.cgal_polygon(), xx, yy) ? 1.0 : -1.0; +} + double signed_distance_2d(const Type2D &obj, double xx, double yy) { return std::visit( diff --git a/src/csg/meson.build b/src/csg/meson.build index dafffc17315de7500a66a35b91efa38696722698..0b5b69506676ea7fff480f48cf75434cedb4adde 100644 --- a/src/csg/meson.build +++ b/src/csg/meson.build @@ -5,7 +5,8 @@ lib_csg_parser = static_library( 'levelset_2d.cpp', 'parser.cpp', 'matrix_functions.cpp', - 'cgal_helper.cpp', + 'cgal_helper_polyhedron.cpp', + 'cgal_helper_polygon.cpp', include_directories: parser_inc, dependencies: [pegtl, cgal], install : true) diff --git a/src/csg/parser.cpp b/src/csg/parser.cpp index ec6209a29bd4e81119c4d51620622cf7c3fc490e..b3a43134aada44895404459a7cab0b7d0c3709fb 100644 --- a/src/csg/parser.cpp +++ b/src/csg/parser.cpp @@ -13,6 +13,8 @@ using namespace tao::pegtl; namespace { +const std::string UNDEFINED_STR = "undef"; + using Attr = std::variant, std::string, std::vector>>; using AttrMap = std::map; @@ -71,6 +73,7 @@ struct S_CLN : pad, space> {}; struct true_literal : string<'t', 'r', 'u', 'e'> {}; struct false_literal : string<'f', 'a', 'l', 's', 'e'> {}; +struct undef_literal : string<'u', 'n', 'd', 'e', 'f'> {}; struct boolean_literal : sor {}; @@ -91,7 +94,7 @@ struct matrix : seq>, R_ARR> {}; struct matrix_attr : matrix {}; struct value : sor {}; + matrix_attr, undef_literal> {}; struct keyval : seq, string<'='>, pad> {}; @@ -115,6 +118,10 @@ struct circle struct square : seq, L_FUN, attr_list, R_FUN> {}; +struct polygon + : seq, L_FUN, attr_list, R_FUN> { +}; + template struct shape; template <> @@ -122,7 +129,7 @@ struct shape : seq, opt> {}; template <> -struct shape : seq, opt> {}; +struct shape : seq, opt> {}; template struct obj_list; @@ -570,6 +577,37 @@ template <> struct action { } }; +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + std::stringstream ss(in.string()); + auto &curr_attr = st.curr_attrs.back(); + + auto points = + std::get>>(curr_attr["points"]); + + std::vector> paths = {{}}; + if (std::holds_alternative(curr_attr["paths"])) { + assert(std::get(curr_attr["paths"]) == UNDEFINED_STR); + } else { + paths = std::get>>(curr_attr["paths"]); + } + + auto diff = csg::Difference2D(); + for (auto it = paths.begin(); it != paths.end(); ++it) { + if (it == paths.begin()) { + diff.first_obj = + std::make_shared(csg::Polygon(points, *it)); + } else { + diff.next_objs.objs.push_back(csg::Polygon(points, *it)); + } + } + + st.current_2d_objs.back().push_back(diff); + st.curr_attrs.pop_back(); + } +}; + template <> struct action { template static void apply(const Input &in, parser_state &st) { @@ -632,6 +670,14 @@ template <> struct action { } }; +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + std::stringstream ss(in.string()); + st.curr_attr[st.current_name] = UNDEFINED_STR; + } +}; + template <> struct action { template static void apply(const Input &in, parser_state &st) { diff --git a/src/csg/tests/levelset/extrude.t.cpp b/src/csg/tests/levelset/extrude.t.cpp index 74404cf9d65c01acff45d1952ff3333cc492531a..d3574a75448dc11618f3d2a6d6b0170585b17658 100644 --- a/src/csg/tests/levelset/extrude.t.cpp +++ b/src/csg/tests/levelset/extrude.t.cpp @@ -1,8 +1,8 @@ #include "catch2/catch.hpp" #include -#include #include +#include namespace { @@ -117,4 +117,60 @@ TEST_CASE("simple torus", "[Levelset Extrude]") { } } +TEST_CASE("Linear extrude of a triangle with hole", "[Levelset Primitives]") { + double ht = 100.0, outer = 100.0, inner = 80.0; + double thick = outer - inner; + + auto my_lin_ext = csg::LinearExtrude{ + .height = ht, .center = true, .scale = {1, 1}, .group = csg::Union2D()}; + + // A 100 x 100 right triangle with a 80 x 80 hole formed by using a polygon + // and extrude + csg::Polygon my_outer_tri({{0, 0}, + {outer, 0}, + {0, outer}, + {thick, thick}, + {inner, thick}, + {thick, inner}}, + {0, 1, 2}); + + csg::Polygon my_inner_tri({{0, 0}, + {outer, 0}, + {0, outer}, + {thick, thick}, + {inner, thick}, + {thick, inner}}, + {3, 4, 5}); + + csg::Union2D my_union; + my_union.objs.push_back(my_inner_tri); + + auto my_diff = csg::Difference2D{ + .first_obj = std::make_shared(my_outer_tri), + .next_objs = csg::Union2D(my_union)}; + + 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); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(0.5 * thick, 0.5 * thick, 1.01 * (ht / 2))); + CHECK_FALSE(0 < my_levelset(0.5 * thick, 0.5 * thick, -1.01 * (ht / 2))); + CHECK_FALSE(0 < my_levelset(2 * thick, 2 * thick, 0.0)); + CHECK_FALSE(0 < my_levelset(4 * thick, 2 * thick, 0.0)); + CHECK_FALSE(0 < my_levelset(1.01 * outer, 0.5 * thick, 0.0)); + CHECK_FALSE(0 < my_levelset(0.5 * thick, 1.01 * outer, 0.0)); + CHECK_FALSE(0 < my_levelset(inner, inner, 0.0)); + } + SECTION("Inside") { + CHECK(0 < my_levelset(0.5 * thick, 0.5 * thick, 0.99 * (ht / 2))); + CHECK(0 < my_levelset(0.5 * thick, 0.5 * thick, -0.99 * (ht / 2))); + CHECK(0 < my_levelset(0.5 * thick, 0.5 * thick, 0.0)); + CHECK(0 < my_levelset(0.7 * outer, 0.5 * thick, 0.0)); + CHECK(0 < my_levelset(0.5 * thick, 0.7 * outer, 0.0)); + } +} + } // namespace diff --git a/src/csg/tests/parser/extrude.t.cpp b/src/csg/tests/parser/extrude.t.cpp index 3cebfeff4a2038a452ea8d4b85a5e2aba3dd9a33..3a2b5640bf10cd6bfeb11c0fc3f92295ff1170c2 100644 --- a/src/csg/tests/parser/extrude.t.cpp +++ b/src/csg/tests/parser/extrude.t.cpp @@ -5,7 +5,7 @@ #include namespace { -TEST_CASE("linear extrude", "[csg]") { +TEST_CASE("linear extrude circle", "[csg]") { auto st = *csg::parse_csg(R"( linear_extrude( height = 10, @@ -123,4 +123,59 @@ linear_extrude(height = 10, center = false, scale = [5, 5]) { CHECK(st == nullptr); } +TEST_CASE("linear extrude polygon without paths", "[csg]") { + auto st = *csg::parse_csg(R"( +linear_extrude( +height = 100, +center = true, +scale = [1, 1]) { +polygon( +points = [[0, 0], [100, 0], [130, 50], [30, 50]], +paths = undef); +} +)"); + auto lin_ext = std::get(st.top.objs.back()); + CHECK(lin_ext.group.objs.size() == 1); + CHECK(lin_ext.height == 100); + CHECK(lin_ext.center == true); + + auto [Sx, Sy] = lin_ext.scale; + CHECK(Sx == 1); + CHECK(Sy == 1); + + auto diff = std::get(lin_ext.group.objs.at(0)); + auto outer = std::get(*diff.first_obj); + + CHECK(diff.next_objs.objs.size() == 0); + CHECK(outer.cgal_polygon().size() == 4); +} + +TEST_CASE("linear extrude polygon with hole", "[csg]") { + auto st = *csg::parse_csg(R"( +linear_extrude( +height = 100, +center = true, +scale = [1, 1]) { +polygon( +points = [[0, 0], [100, 0], [0, 100], + [10, 10], [80, 10], [10, 80]], +paths = [[0, 1, 2], [3, 4, 5]]); +} +)"); + auto lin_ext = std::get(st.top.objs.back()); + CHECK(lin_ext.group.objs.size() == 1); + CHECK(lin_ext.height == 100); + CHECK(lin_ext.center == true); + + auto [Sx, Sy] = lin_ext.scale; + CHECK(Sx == 1); + CHECK(Sy == 1); + + auto diff = std::get(lin_ext.group.objs.at(0)); + auto outer = std::get(*diff.first_obj); + auto inner = std::get(diff.next_objs.objs.at(0)); + CHECK(outer.cgal_polygon().size() == 3); + CHECK(inner.cgal_polygon().size() == 3); +} + } // namespace diff --git a/src/csg_cgal_helper.hpp b/src/csg_cgal_helper.hpp index 52d9aed823128607a308aec6ab7f31445485115a..0326d23b8b50363efaac97ab3733d79595c8072e 100644 --- a/src/csg_cgal_helper.hpp +++ b/src/csg_cgal_helper.hpp @@ -1,20 +1,28 @@ #ifndef CGAL_HELPER_H_ #define CGAL_HELPER_H_ +#include +#include #include #include namespace cgal_helper { -typedef CGAL::Simple_cartesian Kernel; -typedef CGAL::Polyhedron_3 Polyhedron; +typedef CGAL::Simple_cartesian CK; +typedef CGAL::Polyhedron_3 Polyhedron; +typedef CGAL::Exact_predicates_inexact_constructions_kernel IK; +typedef CGAL::Polygon_2 Polygon; Polyhedron create_polyhedron(const std::vector> &points, const std::vector> &faces); -bool inside(const Polyhedron &polyhedron, double xx, double yy, - double zz); +Polygon create_polygon(const std::vector> &points, + const std::vector &path); + +bool inside(const Polyhedron &polyhedron, double xx, double yy, double zz); + +bool inside(const Polygon &polygon, double xx, double yy); } // namespace cgal_helper