From f3b056d23f5f9810f378e8572d353b1f8939eb7b Mon Sep 17 00:00:00 2001 From: Mark Meredith Date: Fri, 24 Apr 2020 10:02:55 -0400 Subject: [PATCH 1/6] Add CSG stuff --- CMakeLists.txt | 6 +- meson.build | 17 +- src/csg/CMakeLists.txt | 40 ++ src/csg/csg.hpp | 52 ++ src/csg/impl/csg.cpp | 28 ++ src/csg/impl/csg_types.hpp | 69 +++ src/csg/impl/levelset.cpp | 135 ++++++ src/csg/impl/parser.cpp | 403 +++++++++++++++ src/csg/meson.build | 7 + src/csg/tests/CMakeLists.txt | 89 ++++ src/csg/tests/levelset/boolean.t.cpp | 92 ++++ src/csg/tests/levelset/primitives.t.cpp | 539 +++++++++++++++++++++ src/csg/tests/levelset/transform.t.cpp | 140 ++++++ src/csg/tests/parser/boolean.t.cpp | 86 ++++ {tests => src/csg/tests/parser}/main.cpp | 0 src/csg/tests/parser/nest.cpp | 35 ++ src/csg/tests/parser/other.t.cpp | 49 ++ src/csg/tests/parser/primitives.t.cpp | 44 ++ src/csg/tests/parser/transform.t.cpp | 133 +++++ src/{ => inputs}/CMakeLists.txt | 0 src/{ => inputs}/geometry.cpp | 0 src/{ => inputs}/inputs.hpp | 0 src/{ => inputs}/mesh.cpp | 0 src/inputs/meson.build | 11 + src/{ => inputs}/parser.cpp | 0 src/{ => inputs}/solver.cpp | 0 src/{ => inputs}/solver.hpp | 0 {tests => src/inputs/tests}/CMakeLists.txt | 0 src/inputs/tests/main.cpp | 2 + src/inputs/tests/meson.build | 9 + {tests => src/inputs/tests}/parser.t.cpp | 0 {tests => src/inputs/tests}/solver.t.cpp | 0 src/{ => inputs}/time.cpp | 0 src/meson.build | 12 - tests/meson.build | 8 - 35 files changed, 1981 insertions(+), 25 deletions(-) create mode 100644 src/csg/CMakeLists.txt create mode 100644 src/csg/csg.hpp create mode 100644 src/csg/impl/csg.cpp create mode 100644 src/csg/impl/csg_types.hpp create mode 100644 src/csg/impl/levelset.cpp create mode 100644 src/csg/impl/parser.cpp create mode 100644 src/csg/meson.build create mode 100644 src/csg/tests/CMakeLists.txt create mode 100644 src/csg/tests/levelset/boolean.t.cpp create mode 100644 src/csg/tests/levelset/primitives.t.cpp create mode 100644 src/csg/tests/levelset/transform.t.cpp create mode 100644 src/csg/tests/parser/boolean.t.cpp rename {tests => src/csg/tests/parser}/main.cpp (100%) create mode 100644 src/csg/tests/parser/nest.cpp create mode 100644 src/csg/tests/parser/other.t.cpp create mode 100644 src/csg/tests/parser/primitives.t.cpp create mode 100644 src/csg/tests/parser/transform.t.cpp rename src/{ => inputs}/CMakeLists.txt (100%) rename src/{ => inputs}/geometry.cpp (100%) rename src/{ => inputs}/inputs.hpp (100%) rename src/{ => inputs}/mesh.cpp (100%) create mode 100644 src/inputs/meson.build rename src/{ => inputs}/parser.cpp (100%) rename src/{ => inputs}/solver.cpp (100%) rename src/{ => inputs}/solver.hpp (100%) rename {tests => src/inputs/tests}/CMakeLists.txt (100%) create mode 100644 src/inputs/tests/main.cpp create mode 100644 src/inputs/tests/meson.build rename {tests => src/inputs/tests}/parser.t.cpp (100%) rename {tests => src/inputs/tests}/solver.t.cpp (100%) rename src/{ => inputs}/time.cpp (100%) delete mode 100644 src/meson.build delete mode 100644 tests/meson.build diff --git a/CMakeLists.txt b/CMakeLists.txt index cdfb12c..8f49098 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,8 @@ if(CCACHE_FOUND) set( CMAKE_CXX_COMPILER_LAUNCHER ccache ) endif() -add_subdirectory(src) +add_subdirectory(src/csg) +add_subdirectory(src/inputs) add_subdirectory(tests) find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang-format) @@ -28,7 +29,8 @@ if(CLANG_FMT) COMMAND ${CLANG_FMT} -i ${_psrcs} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests ) - file(GLOB _includes ${PROJECT_SOURCE_DIR}/src/*.hpp) + file(GLOB _includes ${PROJECT_SOURCE_DIR}/src/csg/*.hpp) + file(GLOB _includes ${PROJECT_SOURCE_DIR}/src/inputs/*.hpp) add_custom_target(fmt) add_dependencies(fmt fmt_test) diff --git a/meson.build b/meson.build index 0b8213f..b2915d6 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,18 @@ project('mfix-parser', 'cpp', tao_inc = include_directories('subprojects/PEGTL/include') catch2_inc = include_directories('subprojects/Catch2/single_include') -parser_inc = include_directories('src') +parser_inc = include_directories( + 'src/csg', + 'src/inputs') -subdir('src') -subdir('tests') +subdir('src/csg') +subdir('src/inputs') + +executable( + 'mfix-parser', + 'src/main.cpp', + include_directories: parser_inc, + link_with: [ + lib_csg_parser, + lib_inputs_parser, + ]) diff --git a/src/csg/CMakeLists.txt b/src/csg/CMakeLists.txt new file mode 100644 index 0000000..c6c500e --- /dev/null +++ b/src/csg/CMakeLists.txt @@ -0,0 +1,40 @@ +################################################################################ +# CSG Parser +################################################################################ +add_library(csg) + +target_sources(csg PRIVATE + impl/parser.cpp + impl/levelset.cpp + impl/csg.cpp + ) + +target_include_directories(csg PRIVATE + ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include) + +target_link_libraries(csg PRIVATE stdc++fs) + +target_compile_features(csg PRIVATE cxx_std_17) + +add_subdirectory(tests) + +find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang-format) + +if(CLANG_FMT) + get_target_property(_ut_srcs unit_tests SOURCES) + get_target_property(_ft_srcs functional_tests SOURCES) + get_target_property(_csg_srcs csg SOURCES) + file(GLOB _csg_incs ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/impl/*.hpp) + + add_custom_target(fmt_csg + COMMAND ${CLANG_FMT} -i ${_csg_srcs} ${_csg_incs} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + add_custom_target(fmt_test + COMMAND ${CLANG_FMT} -i ${_ut_srcs} ${_ft_srcs} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests + ) + + add_custom_target(fmt) + add_dependencies(fmt fmt_test fmt_csg) +endif() diff --git a/src/csg/csg.hpp b/src/csg/csg.hpp new file mode 100644 index 0000000..1862b83 --- /dev/null +++ b/src/csg/csg.hpp @@ -0,0 +1,52 @@ +#ifndef CSG_H_ +#define CSG_H_ + +#if defined(CSG_USE_GPU) && !defined(CSG_GPU_HOST_DEVICE) +#define CSG_GPU_HOST_DEVICE __host__ __device__ +#else +#define CSG_GPU_HOST_DEVICE +#endif + +#include +#include +#include +#include + +namespace csg { + +struct GPUable {}; +template struct IsGPUable : std::false_type {}; +template +struct IsGPUable< + D, typename std::enable_if::value>::type> + : std::true_type {}; + +struct Tree; +std::shared_ptr parse_csg(std::string); + +class CsgIF : GPUable { +public: + CsgIF(std::shared_ptr a_state); + CsgIF(const CsgIF &rhs); + CsgIF(CsgIF &&rhs) noexcept; + CsgIF &operator=(const CsgIF &rhs) = delete; + CsgIF &operator=(CsgIF &&rhs) = delete; + ~CsgIF(); + + CSG_GPU_HOST_DEVICE + double operator()(double xx, double yy, double zz) const noexcept; + + inline double operator()(const std::array &p) const noexcept { + return this->operator()(p[0], p[1], p[2]); + } + +private: + std::shared_ptr m_state; + + class Impl; + std::unique_ptr m_pimpl; +}; + +} // namespace csg + +#endif diff --git a/src/csg/impl/csg.cpp b/src/csg/impl/csg.cpp new file mode 100644 index 0000000..1cb62ee --- /dev/null +++ b/src/csg/impl/csg.cpp @@ -0,0 +1,28 @@ +#include "../csg.hpp" + +#include "csg_types.hpp" + +namespace csg { + +class CsgIF::Impl { +public: + double call_signed_distance(const std::shared_ptr &a_tree, double xx, + double yy, double zz) const { + return signed_distance(a_tree->top, xx, yy, zz); + } +}; + +CsgIF::CsgIF(const CsgIF &rhs) + : m_state(rhs.m_state), m_pimpl(std::make_unique(*rhs.m_pimpl)) {} + +CsgIF::CsgIF(std::shared_ptr a_state) + : m_state(a_state), m_pimpl(std::make_unique()) {} + +CsgIF::CsgIF(CsgIF &&rhs) noexcept = default; +CsgIF::~CsgIF() = default; + +double CsgIF::operator()(double xx, double yy, double zz) const noexcept { + return m_pimpl->call_signed_distance(m_state, xx, yy, zz); +} + +} // namespace csg diff --git a/src/csg/impl/csg_types.hpp b/src/csg/impl/csg_types.hpp new file mode 100644 index 0000000..e70f97b --- /dev/null +++ b/src/csg/impl/csg_types.hpp @@ -0,0 +1,69 @@ +#ifndef CSG_TYPES_H_ +#define CSG_TYPES_H_ + +#include +#include +#include +#include +#include + +namespace csg { + +struct Sphere { + double radius; +}; + +struct Cube { + std::tuple size; + bool center; +}; + +struct Cylinder { + double radius; + double height; + bool center; +}; + +struct Cone { + double radius1; + double radius2; + double height; + bool center; +}; + +struct Mulmatrix; +struct Union; +struct Intersection; +struct Difference; + +using Type = std::variant; + +struct Union { + std::vector objs; +}; + +struct Intersection { + std::vector objs; +}; + +struct Difference { + std::shared_ptr first_obj; + Union next_objs; +}; + +struct Mulmatrix { + std::array, 3> rotation; + std::array translation; + Union group; +}; + +struct Tree { + Union top; +}; + +double signed_distance(const Union &, double, double, double); + +} // namespace csg + +#endif diff --git a/src/csg/impl/levelset.cpp b/src/csg/impl/levelset.cpp new file mode 100644 index 0000000..0f23278 --- /dev/null +++ b/src/csg/impl/levelset.cpp @@ -0,0 +1,135 @@ +#include "csg_types.hpp" + +#include +#include +#include +#include + +namespace { + +const double EXTERNAL_FLOW = -1.0; + +template struct overloaded : Ts... { using Ts::operator()...; }; +template +// clang-format off +overloaded(Ts...) -> overloaded; // not needed as of C++20 +// clang-format on + +} // namespace + +namespace csg { + +double signed_distance(const Cone &, double, double, double); +double signed_distance(const Cube &, double, double, double); +double signed_distance(const Cylinder &, double, double, double); +double signed_distance(const Difference &, double, double, double); +double signed_distance(const Intersection &, double, double, double); +double signed_distance(const Mulmatrix &, double, double, double); +double signed_distance(const Sphere &, double, double, double); +double signed_distance(const Type &, double, double, double); + +double signed_distance(const Union &group, double xx, double yy, double zz) { + auto sdist = -std::numeric_limits::max(); + for (const auto &member : group.objs) { + auto sd = signed_distance(member, xx, yy, zz); + sdist = std::max(sdist, sd); + }; + + return sdist; +} + +double signed_distance(const Intersection &group, double xx, double yy, + double zz) { + auto sdist = std::numeric_limits::max(); + for (const auto &member : group.objs) { + auto sd = signed_distance(member, xx, yy, zz); + sdist = std::min(sdist, sd); + }; + return sdist; +} + +double signed_distance(const Difference &group, double xx, double yy, + double zz) { + auto sdist = signed_distance(*group.first_obj, xx, yy, zz); + + for (const auto &member : group.next_objs.objs) { + auto sd = signed_distance(member, xx, yy, zz); + sdist = std::min(sdist, -sd); + }; + return sdist; +} + +double signed_distance(const Mulmatrix &mm, double xx, double yy, double zz) { + // TODO: Invert non-orthogonal matrices + auto XX = xx - mm.translation[0]; + auto YY = yy - mm.translation[1]; + auto ZZ = zz - mm.translation[2]; + return signed_distance( + mm.group, + mm.rotation[0][0] * XX + mm.rotation[1][0] * YY + mm.rotation[2][0] * ZZ, + mm.rotation[0][1] * XX + mm.rotation[1][1] * YY + mm.rotation[2][1] * ZZ, + mm.rotation[0][2] * XX + mm.rotation[1][2] * YY + mm.rotation[2][2] * ZZ); +} + +double signed_distance(const Cone &cone, double xx, double yy, double zz) { + double ZZ = cone.center ? zz + cone.height / 2 : zz; + double sign_z = (ZZ >= 0 && ZZ <= cone.height) ? -1.0 : 1.0; + auto dist_z = std::min(std::fabs(ZZ), std::fabs(ZZ - cone.height)); + + auto rr = cone.radius1 + (ZZ * (cone.radius2 - cone.radius1) / cone.height); + auto delta_r = xx * xx + yy * yy - rr * rr; + double sign_r = delta_r <= 0 ? -1.0 : 1.0; + auto dist_r = std::fabs(delta_r); + + return EXTERNAL_FLOW * std::max(sign_z * dist_z, sign_r * dist_r); +} + +double signed_distance(const Cube &cube, double xx, double yy, double zz) { + + auto [Lx, Ly, Lz] = cube.size; + auto XX = cube.center ? xx + Lx / 2 : xx; + double sign_x = (XX >= 0 && XX <= Lx) ? -1.0 : 1.0; + auto dist_x = std::min(std::fabs(XX), std::fabs(XX - Lx)); + + auto YY = cube.center ? yy + Ly / 2 : yy; + double sign_y = (YY >= 0 && YY <= Ly) ? -1.0 : 1.0; + auto dist_y = std::min(std::fabs(YY), std::fabs(YY - Ly)); + + auto ZZ = cube.center ? zz + Lz / 2 : zz; + double sign_z = (ZZ >= 0 && ZZ <= Lz) ? -1.0 : 1.0; + auto dist_z = std::min(std::fabs(ZZ), std::fabs(ZZ - Lz)); + + return EXTERNAL_FLOW * + std::max({sign_x * dist_x, sign_y * dist_y, sign_z * dist_z}); +} + +double signed_distance(const Sphere &sph, double xx, double yy, double zz) { + auto delta_r = xx * xx + yy * yy + zz * zz - sph.radius * sph.radius; + double sign = delta_r <= 0 ? -1.0 : 1.0; + auto dist = std::fabs(delta_r); + return EXTERNAL_FLOW * sign * dist; +} + +double signed_distance(const Cylinder &cyl, double xx, double yy, double zz) { + auto ZZ = cyl.center ? zz + cyl.height / 2 : zz; + double sign_z = (ZZ >= 0 && ZZ <= cyl.height) ? -1.0 : 1.0; + auto dist_z = std::min(std::fabs(ZZ), std::fabs(ZZ - cyl.height)); + + auto rr = cyl.radius; + auto delta_r = xx * xx + yy * yy - rr * rr; + double sign_r = delta_r <= 0 ? -1.0 : 1.0; + auto dist_r = std::fabs(delta_r); + + return EXTERNAL_FLOW * std::max(sign_z * dist_z, sign_r * dist_r); +} + +double signed_distance(const Type &obj, double xx, double yy, double zz) { + + return std::visit( + overloaded{ + [xx, yy, zz](auto &&arg) { return signed_distance(arg, xx, yy, zz); }, + }, + obj); +} + +} // namespace csg diff --git a/src/csg/impl/parser.cpp b/src/csg/impl/parser.cpp new file mode 100644 index 0000000..a6cd4b1 --- /dev/null +++ b/src/csg/impl/parser.cpp @@ -0,0 +1,403 @@ +// standard includes +#include +#include +#include + +// subproject includes +#include + +// includes +#include "csg_types.hpp" + +using namespace tao::pegtl; + +namespace { + +struct parser_state { + std::vector> current_objs; + std::vector current_group; + std::string current_name; + std::vector current_vec; + std::vector> current_matrix; + std::vector>> current_matrices; + std::map, std::string>> + curr_attr; +}; + +} // namespace + +namespace { + +struct escaped_x : seq, rep<2, must>> {}; +struct escaped_u : seq, rep<4, must>> {}; +struct escaped_U : seq, rep<8, must>> {}; +struct escaped_c + : one<'\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'> {}; + +struct escaped : sor {}; + +struct character + : if_must_else, escaped, utf8::range<0x20, 0x10FFFF>> {}; +struct string_literal : if_must, until, character>> {}; + +struct plus_minus : opt> {}; +struct dot : one<'.'> {}; + +struct decimal + : if_then_else, seq, opt>>> { +}; +struct e : one<'e', 'E'> {}; +struct p : one<'p', 'P'> {}; +struct exponent : seq> {}; +struct double_ : seq> {}; + +struct padded_double : pad {}; + +struct L_FUN : pad, space> {}; +struct R_FUN : pad, space> {}; +struct L_ARR : pad, space> {}; +struct R_ARR : pad, space> {}; +struct L_BLK : pad, space> {}; +struct R_BLK : pad, space> {}; +struct S_CLN : pad, space> {}; + +struct true_literal : string<'t', 'r', 'u', 'e'> {}; +struct false_literal : string<'f', 'a', 'l', 's', 'e'> {}; + +struct boolean_literal : sor {}; + +struct special_identifier : seq, identifier> {}; + +struct name : sor {}; + +struct vector_cell : padded_double {}; + +struct vector : seq>, R_ARR> {}; + +struct vector_attr : vector {}; + +struct value + : sor {}; + +struct keyval : seq, string<'='>, pad> {}; + +struct attr_list : list> {}; + +struct row : vector {}; + +struct matrix : seq>, R_ARR> {}; + +struct sphere + : seq, L_FUN, attr_list, R_FUN> {}; + +struct cube : seq, L_FUN, attr_list, R_FUN> {}; + +struct cylinder : seq, L_FUN, + attr_list, R_FUN> {}; + +struct shape : seq, opt> {}; + +struct obj_list; + +struct bool_union : seq, L_FUN, R_FUN, L_BLK, + obj_list, R_BLK> {}; + +struct bool_intersection + : seq, + L_FUN, R_FUN, L_BLK, obj_list, R_BLK> {}; + +struct bool_diff : seq, + L_FUN, R_FUN, L_BLK, obj_list, R_BLK> {}; + +struct bool_exp : sor {}; + +struct mulmat : seq, + L_FUN, matrix, R_FUN, L_BLK, obj_list, R_BLK> {}; + +struct group : seq, L_FUN, R_FUN, + opt>, opt> {}; + +struct render : seq, L_FUN, attr_list, + R_FUN, opt>, opt> {}; + +struct colorgroup : seq, L_FUN, vector, R_FUN, + opt>, opt> {}; + +struct csg_obj : sor {}; + +struct obj_list : plus> {}; + +struct grammar : seq {}; + +template struct action {}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + // std::cout << " { " << std::endl; + std::vector new_group; + st.current_objs.push_back(new_group); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + // std::cout << " } " << std::endl; + st.current_group.clear(); + for (auto obj : st.current_objs.back()) { + st.current_group.push_back(obj); + } + st.current_objs.pop_back(); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + std::stringstream ss(in.string()); + double cell; + ss >> cell; + st.current_vec.push_back(cell); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + st.current_matrices.push_back(st.current_matrix); + st.current_matrix.clear(); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + st.current_matrix.push_back(st.current_vec); + st.current_vec.clear(); + } +}; + +void add_group(parser_state &st) { + csg::Union group; + for (const auto &curr_obj : st.current_group) { + group.objs.push_back(curr_obj); + } + st.current_objs.back().push_back(group); +} + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + add_group(st); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + add_group(st); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + add_group(st); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + add_group(st); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + auto csg_in = csg::Intersection(); + for (const auto &curr_obj : st.current_group) { + csg_in.objs.push_back(curr_obj); + } + st.current_objs.back().push_back(csg_in); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + auto csg_diff = csg::Difference(); + for (auto it = st.current_group.begin(); it != st.current_group.end(); + ++it) { + if (it == st.current_group.begin()) { + csg_diff.first_obj = std::make_shared(*it); + } else { + csg_diff.next_objs.objs.push_back(*it); + } + } + st.current_objs.back().push_back(csg_diff); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + auto mulmat = csg::Mulmatrix(); + auto mat = st.current_matrices.back(); + // clang-format off + mulmat.rotation[0] = {mat[0][0], mat[0][1], mat[0][2]}; + mulmat.rotation[1] = {mat[1][0], mat[1][1], mat[1][2]}; + mulmat.rotation[2] = {mat[2][0], mat[2][1], mat[2][2]}; + // clang-format on + + mulmat.translation = { + mat[0][3], + mat[1][3], + mat[2][3], + }; + + for (const auto &curr_obj : st.current_group) { + mulmat.group.objs.push_back(curr_obj); + } + st.current_objs.back().push_back(mulmat); + st.current_matrices.pop_back(); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + csg::Cube cub; + auto size = std::get>(st.curr_attr["size"]); + cub.size = {size[0], size[1], size[2]}; + cub.center = std::get(st.curr_attr["center"]); + + st.current_objs.back().push_back(cub); + st.curr_attr.clear(); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + if (st.curr_attr.count("r")) { + // proper cylinder + if (st.curr_attr.count("r1") || st.curr_attr.count("r2")) { + std::cout << " ERROR: cannot specify both r and (r1 or r2); ambiguous " + << std::endl; + } + csg::Cylinder cyl; + cyl.center = std::get(st.curr_attr["center"]); + cyl.height = std::get(st.curr_attr["h"]); + cyl.radius = std::get(st.curr_attr["r"]); + st.current_objs.back().push_back(cyl); + + } else { + // conic "cylinder" + csg::Cone cone; + cone.center = std::get(st.curr_attr["center"]); + cone.height = std::get(st.curr_attr["h"]); + cone.radius1 = std::get(st.curr_attr["r1"]); + cone.radius2 = std::get(st.curr_attr["r2"]); + st.current_objs.back().push_back(cone); + } + + st.curr_attr.clear(); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + csg::Sphere sph; + sph.radius = std::get(st.curr_attr["r"]); + + st.current_objs.back().push_back(sph); + st.curr_attr.clear(); + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + std::stringstream ss(in.string()); + ss >> st.current_name; + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + std::stringstream ss(in.string()); + std::string v; + ss >> v; + st.curr_attr[st.current_name] = v; + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + std::stringstream ss(in.string()); + double v; + ss >> v; + st.curr_attr[st.current_name] = v; + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + st.curr_attr[st.current_name] = true; + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + st.curr_attr[st.current_name] = false; + } +}; + +template <> struct action { + template + static void apply(const Input &in, parser_state &st) { + st.curr_attr[st.current_name] = st.current_vec; + st.current_vec.clear(); + } +}; + +std::optional do_parse(std::string str) { + parser_state st; + std::vector new_group; + st.current_objs.push_back(new_group); + memory_input in(str, "std::cin"); + if (!parse(in, st)) { + return std::nullopt; + } + return st; +} + +} // namespace + +namespace csg { + +std::shared_ptr parse_csg(std::string str) { + auto maybe_state = do_parse(str); + if (!maybe_state.has_value()) { + return nullptr; + } + auto st = maybe_state.value(); + + assert(st.current_objs.size() == 1); + Tree tree; + for (auto obj : st.current_objs.back()) { + tree.top.objs.push_back(obj); + } + assert(tree.top.objs.size() > 0); // Disallow empty .csg file + return std::make_shared(tree); +} +} // namespace csg diff --git a/src/csg/meson.build b/src/csg/meson.build new file mode 100644 index 0000000..7c9fe91 --- /dev/null +++ b/src/csg/meson.build @@ -0,0 +1,7 @@ +lib_csg_parser = static_library( + 'csg-parser', + 'impl/csg.cpp', + 'impl/levelset.cpp', + 'impl/parser.cpp', + include_directories: tao_inc, + install : true) diff --git a/src/csg/tests/CMakeLists.txt b/src/csg/tests/CMakeLists.txt new file mode 100644 index 0000000..87f5205 --- /dev/null +++ b/src/csg/tests/CMakeLists.txt @@ -0,0 +1,89 @@ +################################################################################ +# CSG Parser Tests +################################################################################ + +add_executable(unit_tests EXCLUDE_FROM_ALL + levelset/boolean.t.cpp + levelset/primitives.t.cpp + levelset/transform.t.cpp + parser/boolean.t.cpp + parser/main.cpp + parser/nest.cpp + parser/other.t.cpp + parser/primitives.t.cpp + parser/transform.t.cpp + ) + +add_executable(functional_tests EXCLUDE_FROM_ALL + benchmarks/05-cyl-fluidbed.cpp + benchmarks/07-hopper.cpp + benchmarks/08-cyclone.cpp + benchmarks/02-settling.cpp + benchmarks/tutorial-clr-prototype.cpp + benchmarks/main.cpp + ) + +target_include_directories(unit_tests PRIVATE + ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include + ${CMAKE_SOURCE_DIR}/src/eb/csg + ${CMAKE_SOURCE_DIR}/src/eb/csg/impl + ) +target_include_directories(functional_tests PRIVATE + ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include + ${CMAKE_SOURCE_DIR}/src/eb/csg + amrex + ) + +target_compile_features(unit_tests PRIVATE cxx_std_17) + +# Special treatment for CUDA in functional_tests +if(ENABLE_CUDA) + target_compile_definitions(functional_tests PRIVATE CSG_USE_GPU) + get_target_property(_src_cpp functional_tests SOURCES) + list(FILTER _src_cpp INCLUDE REGEX "\.cpp") + set_source_files_properties(${_src_cpp} PROPERTIES LANGUAGE CUDA ) + set_target_properties( functional_tests + PROPERTIES + CUDA_SEPARABLE_COMPILATION ON # This add -dc flag + ) +else() + target_compile_features(functional_tests PRIVATE cxx_std_17) +endif() + +get_filename_component(TUTORIAL_CLR_CSG + "${PROJECT_SOURCE_DIR}/tutorials/clr/prototype/clr.csg" + ABSOLUTE) +target_compile_definitions(functional_tests + PRIVATE TUTORIAL_CLR_CSG="${TUTORIAL_CLR_CSG}") + +SET(catch2_dir ${PROJECT_SOURCE_DIR}/subprojects/Catch2) + +find_package(Git QUIET) +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + # Update submodules as needed + option(GIT_SUBMODULE "Check submodules during build" ON) + if(GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() +endif() + +if(NOT EXISTS "${catch2_dir}/CMakeLists.txt") + message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") +endif() + +add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build) +list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/") + +target_link_libraries(unit_tests Catch2::Catch2 csg) +target_link_libraries(functional_tests Catch2::Catch2 AMReX::amrex csg) + +include(CTest) +include(Catch) +catch_discover_tests(unit_tests) +catch_discover_tests(functional_tests) diff --git a/src/csg/tests/levelset/boolean.t.cpp b/src/csg/tests/levelset/boolean.t.cpp new file mode 100644 index 0000000..461d254 --- /dev/null +++ b/src/csg/tests/levelset/boolean.t.cpp @@ -0,0 +1,92 @@ +#include "catch2/catch.hpp" + +#include +#include + +#include + +namespace { + +TEST_CASE("Union", "[Levelset Boolean]") { + // Create a union of a cube of size 12 and a sphere of radius 8 + csg::Cube my_cub{.size = {12, 12, 12}, .center = true}; + csg::Sphere my_sph{.radius = 8}; + + auto my_union = csg::Union(); + my_union.objs.push_back(my_cub); + my_union.objs.push_back(my_sph); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_union); + csg::CsgIF my_levelset(my_tree); + + CHECK(0 < my_levelset(0, 7, 0)); + CHECK(Approx(8 * 8 - 7 * 7) == my_levelset(0, 7, 0)); + + CHECK(0 < my_levelset(0, 0, -7.5)); + CHECK(Approx(8 * 8 - 7.5 * 7.5) == my_levelset(0, 0, -7.5)); + + CHECK_FALSE(0 < my_levelset(0, -9, 0)); + CHECK_FALSE(Approx(-1) == my_levelset(0, -9, 0)); + + CHECK_FALSE(0 < my_levelset(9, 0, 0)); + CHECK_FALSE(Approx(-1) == my_levelset(9, 0, 0)); +} + +TEST_CASE("Intersection", "[Levelset Boolean]") { + // Create an of a cube of size 12 and a sphere of radius 8 + csg::Cube my_cub{.size = {12, 12, 12}, .center = true}; + csg::Sphere my_sph{.radius = 8}; + + auto my_in = csg::Intersection(); + my_in.objs.push_back(my_cub); + my_in.objs.push_back(my_sph); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_in); + csg::CsgIF my_levelset(my_tree); + + CHECK_FALSE(0 < my_levelset(0, 7, 0)); + CHECK(Approx(-1) == my_levelset(0, 7, 0)); + + CHECK_FALSE(0 < my_levelset(0, 0, -7.5)); + CHECK(Approx(-1.5) == my_levelset(0, 0, -7.5)); + + CHECK(0 < my_levelset(0, 5, 0)); + CHECK(Approx(1) == my_levelset(0, 5, 0)); + + CHECK(0 < my_levelset(-5, 0, 0)); + CHECK(Approx(1) == my_levelset(-5, 0, 0)); +} + +TEST_CASE("Difference", "[Levelset Boolean]") { + // Create an of a cube of size 12 and remove a sphere of radius 8 + csg::Cube my_cub{.size = {12, 12, 12}, .center = true}; + csg::Sphere my_sph{.radius = 8}; + + csg::Union my_union; + my_union.objs.push_back(my_sph); + + auto my_diff = csg::Difference({ + std::make_shared(my_cub), + csg::Union(my_union), + }); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_diff); + csg::CsgIF my_levelset(my_tree); + + CHECK_FALSE(0 < my_levelset(0, 0, 0)); + CHECK(Approx(-(8 * 8)) == my_levelset(0, 0, 0)); + + CHECK_FALSE(0 < my_levelset(-4, 0, 0)); + CHECK(Approx(-(8 * 8 - 4 * 4)) == my_levelset(-4, 0, 0)); + + CHECK(0 < my_levelset(5.99, 5.99, 0)); + CHECK(Approx(0.01) == my_levelset(5.99, 5.99, 0)); + + CHECK(0 < my_levelset(-5.99, 0, 5.99)); + CHECK(Approx(0.01) == my_levelset(-5.99, 0, 5.99)); +} + +} // namespace diff --git a/src/csg/tests/levelset/primitives.t.cpp b/src/csg/tests/levelset/primitives.t.cpp new file mode 100644 index 0000000..5a02af4 --- /dev/null +++ b/src/csg/tests/levelset/primitives.t.cpp @@ -0,0 +1,539 @@ +#include "catch2/catch.hpp" + +#include +#include + +namespace { + +TEST_CASE("Cylinder", "[Levelset Primitives]") { + + csg::Cylinder my_cyl{.radius = 1, .height = 2, .center = false}; + + SECTION("Not Centered") { + my_cyl.center = false; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cyl); + csg::CsgIF my_levelset(my_tree); + + SECTION("Axis") { + CHECK_FALSE(0 < my_levelset(0, 0, -1)); + CHECK(Approx(-1) == my_levelset(0, 0, -1)); + + CHECK(0 < my_levelset(0, 0, 1)); + CHECK(Approx(1) == my_levelset(0, 0, 1)); + + CHECK_FALSE(0 < my_levelset(0, 0, 3)); + CHECK(Approx(-1) == my_levelset(0, 0, 3)); + } + + SECTION("Cross section") { + CHECK_FALSE(0 < my_levelset(-0.8, 0.8, 1)); + CHECK(Approx(-0.28) == my_levelset(-0.8, 0.8, 1)); + + CHECK_FALSE(0 < my_levelset(0.8, -0.8, 1)); + CHECK(Approx(-0.28) == my_levelset(0.8, -0.8, 1)); + + CHECK(0 < my_levelset(0, 0.8, 1)); + CHECK(Approx(0.36) == my_levelset(0, 0.8, 1)); + + CHECK(0 < my_levelset(0.8, 0, 1)); + CHECK(Approx(0.36) == my_levelset(0.8, 0, 1)); + } + } + SECTION("Centered") { + my_cyl.center = true; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cyl); + csg::CsgIF my_levelset(my_tree); + + SECTION("Axis") { + CHECK_FALSE(0 < my_levelset(0, 0, -2)); + CHECK(Approx(-1) == my_levelset(0, 0, -2)); + + CHECK(0 < my_levelset(0, 0, 0)); + CHECK(Approx(1) == my_levelset(0, 0, 0)); + + CHECK_FALSE(0 < my_levelset(0, 0, 2)); + CHECK(Approx(-1) == my_levelset(0, 0, 2)); + } + + SECTION("Cross section") { + CHECK_FALSE(0 < my_levelset(-0.8, 0.8, 0)); + CHECK(Approx(-0.28) == my_levelset(-0.8, 0.8, 0)); + + CHECK_FALSE(0 < my_levelset(0.8, -0.8, 0)); + CHECK(Approx(-0.28) == my_levelset(0.8, -0.8, 0)); + + CHECK(0 < my_levelset(0, 0.8, 0)); + CHECK(Approx(0.36) == my_levelset(0, 0.8, 0)); + + CHECK(0 < my_levelset(0.8, 0, 0)); + CHECK(Approx(0.36) == my_levelset(0.8, 0, 0)); + } + } +} + +TEST_CASE("Cube", "[Levelset Primitives]") { + + csg::Cube my_cub{.size = {2, 3, 5}, .center = false}; + + SECTION("Not Centered") { + my_cub.center = false; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cub); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(1, 1, -1)); + CHECK(Approx(-1) == my_levelset(1, 1, -1)); + + CHECK_FALSE(0 < my_levelset(1, -1, 1)); + CHECK(Approx(-1) == my_levelset(1, -1, 1)); + + CHECK_FALSE(0 < my_levelset(-1, 1, 1)); + CHECK(Approx(-1) == my_levelset(-1, 1, 1)); + + CHECK_FALSE(0 < my_levelset(3, 1, 1)); + CHECK(Approx(-1) == my_levelset(3, 1, 1)); + + CHECK_FALSE(0 < my_levelset(1, 4, 1)); + CHECK(Approx(-1) == my_levelset(1, 4, 1)); + + CHECK_FALSE(0 < my_levelset(1, 1, 6)); + CHECK(Approx(-1) == my_levelset(1, 1, 6)); + } + SECTION("Inside") { + CHECK(0 < my_levelset(1, 1, 4)); + CHECK(Approx(1) == my_levelset(1, 1, 4)); + + CHECK(0 < my_levelset(1, 2, 1)); + CHECK(Approx(1) == my_levelset(1, 2, 1)); + + CHECK(0 < my_levelset(1, 1, 1)); + CHECK(Approx(1) == my_levelset(1, 1, 1)); + } + } + + SECTION("Centered") { + my_cub.center = true; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cub); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(-0.5, 1, -3)); + CHECK(Approx(-0.5) == my_levelset(-0.5, 1, -3)); + + CHECK_FALSE(0 < my_levelset(-0.5, 2, -2)); + CHECK(Approx(-0.5) == my_levelset(-0.5, 2, -2)); + + CHECK_FALSE(0 < my_levelset(-1.5, 1, -2)); + CHECK(Approx(-0.5) == my_levelset(-1.5, 1, -2)); + + CHECK_FALSE(0 < my_levelset(0.5, -1, 3)); + CHECK(Approx(-0.5) == my_levelset(0.5, -1, 3)); + + CHECK_FALSE(0 < my_levelset(0.5, -2, 2)); + CHECK(Approx(-0.5) == my_levelset(0.5, -2, 2)); + + CHECK_FALSE(0 < my_levelset(1.5, -1, 2)); + CHECK(Approx(-0.5) == my_levelset(1.5, -1, 2)); + } + SECTION("Inside") { + CHECK(0 < my_levelset(0.5, -1, 2)); + CHECK(Approx(0.5) == my_levelset(0.5, -1, 2)); + + CHECK(0 < my_levelset(-0.5, 1, -2)); + CHECK(Approx(0.5) == my_levelset(-0.5, 1, -2)); + } + } +} + +TEST_CASE("Sphere", "[Levelset Primitives]") { + + csg::Sphere my_sph{.radius = 10}; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_sph); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(0, 8, 8)); + CHECK(Approx(-28) == my_levelset(0, 8, 8)); + + CHECK_FALSE(0 < my_levelset(-8, 0, 8)); + CHECK(Approx(-28) == my_levelset(-8, 0, 8)); + + CHECK_FALSE(0 < my_levelset(0, 8, -8)); + CHECK(Approx(-28) == my_levelset(0, 8, -8)); + } + SECTION("Inside") { + CHECK(0 < my_levelset(0, 0, 0)); + CHECK(Approx(100) == my_levelset(0, 0, 0)); + + CHECK(0 < my_levelset(0, 7, 7)); + CHECK(Approx(2) == my_levelset(0, 7, 7)); + + CHECK(0 < my_levelset(-7, 0, 7)); + CHECK(Approx(2) == my_levelset(-7, 0, 7)); + + CHECK(0 < my_levelset(0, 7, -7)); + CHECK(Approx(2) == my_levelset(0, 7, -7)); + } +} +TEST_CASE("Cone", "[Levelset Primitives]") { + + SECTION("radius1 < radius 2; regular)") { + + csg::Cone my_cone{.radius1 = 0, .radius2 = 1, .height = 2, .center = false}; + + SECTION("Untruncated") { + my_cone.radius1 = 0; + + SECTION("Not Centered") { + my_cone.center = false; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cone); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(0.15, -0.15, 0.1)); + CHECK( + Approx((0.5 * 0.1) * (0.5 * 0.1) - (0.15 * 0.15 + 0.15 * .15)) == + my_levelset(0.15, -0.15, 0.1)); + + CHECK_FALSE(0 < my_levelset(-0.15, 0.15, 0.1)); + CHECK( + Approx((0.5 * 0.1) * (0.5 * 0.1) - (0.15 * 0.15 + 0.15 * .15)) == + my_levelset(-0.15, 0.15, 0.1)); + + CHECK_FALSE(0 < my_levelset(0, 0.99, 0.9)); + CHECK(Approx((0.5 * 0.9) * (0.5 * 0.9) - 0.99 * 0.99) == + my_levelset(0, 0.99, 0.9)); + + CHECK_FALSE(0 < my_levelset(-0.99, 0, 0.9)); + CHECK(Approx((0.5 * 0.9) * (0.5 * 0.9) - 0.99 * 0.99) == + my_levelset(-0.99, 0, 0.9)); + + CHECK_FALSE(0 < my_levelset(0, 0, -0.1)); + CHECK(Approx(-0.1) == my_levelset(0, 0, -0.1)); + + CHECK_FALSE(0 < my_levelset(0, 0, 2.1)); + CHECK(Approx(-0.1) == my_levelset(0, 0, 2.1)); + } + + SECTION("Inside") { + CHECK(0 < my_levelset(0, 0, 0.1)); + CHECK(Approx((0.5 * 0.1) * (0.5 * 0.1)) == my_levelset(0, 0, 0.1)); + + CHECK(0 < my_levelset(0, 0, 1.9)); + CHECK(Approx(0.1) == my_levelset(0, 0, 1.9)); + + CHECK(0 < my_levelset(0.01, 0, 0.1)); + CHECK(Approx((0.5 * 0.1) * (0.5 * 0.1) - 0.01 * 0.01) == + my_levelset(0.01, 0, 0.1)); + + CHECK(0 < my_levelset(0, -0.01, 0.1)); + CHECK(Approx((0.5 * 0.1) * (0.5 * 0.1) - 0.01 * 0.01) == + my_levelset(0, -0.01, 0.1)); + + CHECK(0 < my_levelset(0, 0.4, 0.99)); + CHECK(Approx((0.5 * 0.99) * (0.5 * 0.99) - 0.4 * 0.4) == + my_levelset(0, 0.4, 0.99)); + + CHECK(0 < my_levelset(-0.4, 0, 0.9)); + CHECK(Approx((0.5 * 0.9) * (0.5 * 0.9) - 0.4 * 0.4) == + my_levelset(-0.4, 0, 0.9)); + } + } + + SECTION("Centered") { + my_cone.center = true; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cone); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(0.15, -0.15, -0.9)); + CHECK( + Approx((0.5 * 0.1) * (0.5 * 0.1) - (0.15 * 0.15 + 0.15 * 0.15)) == + my_levelset(0.15, -0.15, -0.9)); + + CHECK_FALSE(0 < my_levelset(-0.15, 0.15, -0.9)); + CHECK( + Approx((0.5 * 0.1) * (0.5 * 0.1) - (0.15 * 0.15 + 0.15 * 0.15)) == + my_levelset(-0.15, 0.15, -0.9)); + + CHECK_FALSE(0 < my_levelset(0, 0.99, -0.1)); + CHECK(Approx((0.5 * 0.9) * (0.5 * 0.9) - 0.99 * 0.99) == + my_levelset(0, 0.99, -0.1)); + + CHECK_FALSE(0 < my_levelset(-0.99, 0, -0.1)); + CHECK(Approx((0.5 * 0.9) * (0.5 * 0.9) - 0.99 * 0.99) == + my_levelset(-0.99, 0, -0.1)); + + CHECK_FALSE(0 < my_levelset(0, 0, -1.1)); + CHECK(Approx(-0.1) == my_levelset(0, 0, -1.1)); + + CHECK_FALSE(0 < my_levelset(0, 0, 1.1)); + CHECK(Approx(-0.1) == my_levelset(0, 0, 1.1)); + } + + SECTION("Inside") { + CHECK(0 < my_levelset(0, 0, -0.9)); + CHECK(Approx((0.5 * 0.1) * (0.5 * 0.1)) == my_levelset(0, 0, -0.9)); + + CHECK(0 < my_levelset(0, 0, 0.9)); + CHECK(Approx(0.1) == my_levelset(0, 0, 0.9)); + + CHECK(0 < my_levelset(0.01, 0, -0.9)); + CHECK(Approx((0.5 * 0.1) * (0.5 * 0.1) - 0.01 * 0.01) == + my_levelset(0.01, 0, -0.9)); + + CHECK(0 < my_levelset(0, -0.01, -0.9)); + CHECK(Approx((0.5 * 0.1) * (0.5 * 0.1) - 0.01 * 0.01) == + my_levelset(0, -0.01, -0.9)); + + CHECK(0 < my_levelset(0, 0.4, -0.01)); + CHECK(Approx((0.5 * 0.99) * (0.5 * 0.99) - 0.4 * 0.4) == + my_levelset(0, 0.4, -0.01)); + + CHECK(0 < my_levelset(-0.4, 0, -0.1)); + CHECK(Approx((0.5 * 0.9) * (0.5 * 0.9) - 0.4 * 0.4) == + my_levelset(-0.4, 0, -0.1)); + } + } + } + + SECTION("Truncated") { + my_cone.radius1 = 0.6; + + SECTION("Not Centered") { + my_cone.center = false; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cone); + csg::CsgIF my_levelset(my_tree); + + SECTION("smaller end") { + CHECK_FALSE(0 < my_levelset(0, 0, -0.01)); + CHECK(Approx(-0.01) == my_levelset(0, 0, -0.01)); + + CHECK(0 < my_levelset(0, 0, 0.01)); + CHECK(Approx(0.01) == my_levelset(0, 0, 0.01)); + } + SECTION("curved surface near smaller end") { + CHECK_FALSE(0 < my_levelset(0.61, 0, 0.01)); + CHECK(Approx((0.6 + 0.2 * 0.01) * (0.6 + 0.2 * 0.01) - 0.61 * 0.61) == + my_levelset(0.61, 0, 0.01)); + + CHECK(0 < my_levelset(0.59, 0, 0.01)); + CHECK(Approx(0.01) == my_levelset(0.59, 0, 0.01)); + + CHECK_FALSE(0 < my_levelset(-0.5, 0.5, 0.4)); + CHECK(Approx((0.6 + 0.2 * 0.4) * (0.6 + 0.2 * 0.4) - + (0.5 * 0.5 + 0.5 * 0.5)) == my_levelset(-0.5, 0.5, 0.4)); + + CHECK(0 < my_levelset(-0.3, 0.5, 0.4)); + CHECK(Approx((0.6 + 0.2 * 0.4) * (0.6 + 0.2 * 0.4) - + (0.3 * 0.3 + 0.5 * 0.5)) == my_levelset(-0.3, 0.5, 0.4)); + } + SECTION("curved surface near the middle") { + CHECK_FALSE(0 < my_levelset(0.73, -0.3, 0.8)); + CHECK(Approx((0.6 + 0.2 * 0.8) * (0.6 + 0.2 * 0.8) - + (0.73 * 0.73 + 0.3 * 0.3)) == + my_levelset(0.73, -0.3, 0.8)); + + CHECK(0 < my_levelset(0.63, -0.3, 0.8)); + CHECK(Approx((0.6 + 0.2 * 0.8) * (0.6 + 0.2 * 0.8) - + (0.63 * 0.63 + 0.3 * 0.3)) == + my_levelset(0.63, -0.3, 0.8)); + + CHECK_FALSE(0 < my_levelset(-0.9, 0.3, 1.4)); + CHECK(Approx((0.6 + 0.2 * 1.4) * (0.6 + 0.2 * 1.4) - + (0.9 * 0.9 + 0.3 * 0.3)) == my_levelset(-0.9, 0.3, 1.4)); + + CHECK(0 < my_levelset(-0.8, 0.3, 1.4)); + CHECK(Approx((0.6 + 0.2 * 1.4) * (0.6 + 0.2 * 1.4) - + (0.8 * 0.8 + 0.3 * 0.3)) == my_levelset(-0.8, 0.3, 1.4)); + } + SECTION("larger end") { + CHECK_FALSE(0 < my_levelset(0, 0, 2.01)); + CHECK(Approx(-0.01) == my_levelset(0, 0, 2.01)); + + CHECK(0 < my_levelset(0, 0, 1.99)); + CHECK(Approx(0.01) == my_levelset(0, 0, 1.99)); + } + SECTION("curved surface near larger end") { + CHECK_FALSE(0 < my_levelset(1.1, 0, 1.9)); + CHECK(Approx((0.6 + 0.2 * 1.9) * (0.6 + 0.2 * 1.9) - 1.1 * 1.1) == + my_levelset(1.1, 0, 1.9)); + + CHECK(0 < my_levelset(0.9, 0, 1.9)); + CHECK(Approx(0.1) == my_levelset(0.9, 0, 1.9)); + + CHECK_FALSE(0 < my_levelset(1, -0.3, 1.9)); + CHECK(Approx((0.6 + 0.2 * 1.9) * (0.6 + 0.2 * 1.9) - + (0.3 * 0.3 + 1 * 1)) == my_levelset(1, -0.3, 1.9)); + + CHECK(0 < my_levelset(0.9, -0.3, 1.9)); + CHECK(Approx((0.6 + 0.2 * 1.9) * (0.6 + 0.2 * 1.9) - + (0.3 * 0.3 + 0.9 * 0.9)) == my_levelset(0.9, -0.3, 1.9)); + } + } + + SECTION("Centered") { + my_cone.center = true; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cone); + csg::CsgIF my_levelset(my_tree); + + SECTION("smaller end") { + CHECK_FALSE(0 < my_levelset(0, 0, -1.01)); + CHECK(Approx(-0.01) == my_levelset(0, 0, -1.01)); + + CHECK(0 < my_levelset(0, 0, -0.99)); + CHECK(Approx(0.01) == my_levelset(0, 0, -0.99)); + + CHECK(0 < my_levelset(0, 0, -0.99)); + CHECK(Approx(0.01) == my_levelset(0, 0, -0.99)); + } + SECTION("curved surface near smaller end") { + CHECK_FALSE(0 < my_levelset(0.61, 0, -0.99)); + CHECK(Approx((0.6 + 0.2 * 0.01) * (0.6 + 0.2 * 0.01) - 0.61 * 0.61) == + my_levelset(0.61, 0, -0.99)); + + CHECK(0 < my_levelset(0.5, 0, -0.99)); + CHECK(Approx(0.01) == my_levelset(0.5, 0, -0.99)); + + CHECK_FALSE(0 < my_levelset(0.65, -0.1, -0.9)); + CHECK(Approx((0.6 + 0.2 * 0.1) * (0.6 + 0.2 * 0.1) - + (0.65 * 0.65 + 0.1 * 0.1)) == + my_levelset(0.65, -0.1, -0.9)); + + CHECK(0 < my_levelset(0.57, -0.1, -0.9)); + CHECK(Approx((0.6 + 0.2 * 0.1) * (0.6 + 0.2 * 0.1) - + (0.57 * 0.57 + 0.1 * 0.1)) == + my_levelset(0.57, -0.1, -0.9)); + } + SECTION("curved surface near middle") { + CHECK_FALSE(0 < my_levelset(-0.9, 0.3, 0.2)); + CHECK(Approx((0.6 + 0.2 * 1.2) * (0.6 + 0.2 * 1.2) - + (0.9 * 0.9 + 0.3 * 0.3)) == my_levelset(-0.9, 0.3, 0.2)); + + CHECK(0 < my_levelset(-0.7, 0.3, 0.2)); + CHECK(Approx((0.6 + 0.2 * 1.2) * (0.6 + 0.2 * 1.2) - + (0.7 * 0.7 + 0.3 * 0.3)) == my_levelset(-0.7, 0.3, 0.2)); + + CHECK_FALSE(0 < my_levelset(0.75, -0.3, -0.2)); + CHECK(Approx((0.6 + 0.2 * 0.8) * (0.6 + 0.2 * 0.8) - + (0.75 * 0.75 + 0.3 * 0.3)) == + my_levelset(0.75, -0.3, -0.2)); + + CHECK(0 < my_levelset(0.6, -0.3, -0.2)); + CHECK(Approx((0.6 + 0.2 * 0.8) * (0.6 + 0.2 * 0.8) - + (0.6 * 0.6 + 0.3 * 0.3)) == + my_levelset(0.6, -0.3, -0.2)); + } + SECTION("larger end") { + CHECK_FALSE(0 < my_levelset(0, 0, 1.01)); + CHECK(Approx(-0.01) == my_levelset(0, 0, 1.01)); + + CHECK(0 < my_levelset(0, 0, 0.99)); + CHECK(Approx(0.01) == my_levelset(0, 0, 0.99)); + } + SECTION("curved surface near the larger end") { + CHECK_FALSE(0 < my_levelset(1.1, 0, 0.95)); + CHECK(Approx((0.6 + 0.2 * 1.95) * (0.6 + 0.2 * 1.95) - 1.1 * 1.1) == + my_levelset(1.1, 0, 0.95)); + + CHECK(0 < my_levelset(0.8, 0, 0.95)); + CHECK(Approx(0.05) == my_levelset(0.8, 0, 0.95)); + + CHECK_FALSE(0 < my_levelset(1.0, -0.3, 0.8)); + CHECK(Approx((0.6 + 0.2 * 1.8) * (0.6 + 0.2 * 1.8) - + (0.3 * 0.3 + 1 * 1)) == my_levelset(1.0, -0.3, 0.8)); + + CHECK(0 < my_levelset(0.73, -0.3, 0.8)); + CHECK(Approx(0.2) == my_levelset(0.73, -0.3, 0.8)); + } + } + } + } + + SECTION("radius1 > radius2; inverted") { + csg::Cone my_cone{.radius1 = 1, .radius2 = 0, .height = 2, .center = false}; + + SECTION("Untruncated") { + my_cone.radius2 = 0; + + SECTION("Not Centered") { + my_cone.center = false; + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_cone); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(0.15, -0.15, 1.9)); + CHECK(Approx((1 - 0.5 * 1.9) * (1 - 0.5 * 1.9) - + (0.15 * 0.15 + 0.15 * .15)) == + my_levelset(0.15, -0.15, 1.9)); + + CHECK_FALSE(0 < my_levelset(-0.15, 0.15, 1.9)); + CHECK(Approx((1 - 0.5 * 1.9) * (1 - 0.5 * 1.9) - + (0.15 * 0.15 + 0.15 * .15)) == + my_levelset(-0.15, 0.15, 1.9)); + + CHECK_FALSE(0 < my_levelset(0, 0.99, 1.1)); + CHECK(Approx((1 - 0.5 * 1.1) * (1 - 0.5 * 1.1) - 0.99 * 0.99) == + my_levelset(0, 0.99, 1.1)); + + CHECK_FALSE(0 < my_levelset(-0.99, 0, 1.1)); + CHECK(Approx((1 - 0.5 * 1.1) * (1 - 0.5 * 1.1) - 0.99 * 0.99) == + my_levelset(-0.99, 0, 1.1)); + + CHECK_FALSE(0 < my_levelset(0, 0, -0.1)); + CHECK(Approx(-0.1) == my_levelset(0, 0, -0.1)); + + CHECK_FALSE(0 < my_levelset(0, 0, 2.1)); + CHECK(Approx(-0.1) == my_levelset(0, 0, 2.1)); + } + + SECTION("Inside") { + CHECK(0 < my_levelset(0, 0, 1.9)); + CHECK(Approx((1 - 0.5 * 1.9) * (1 - 0.5 * 1.9)) == + my_levelset(0, 0, 1.9)); + + CHECK(0 < my_levelset(0, 0, 0.1)); + CHECK(Approx(0.1) == my_levelset(0, 0, 0.1)); + + CHECK(0 < my_levelset(0.01, 0, 1.9)); + CHECK(Approx((1 - 0.5 * 1.9) * (1 - 0.5 * 1.9) - 0.01 * 0.01) == + my_levelset(0.01, 0, 1.9)); + + CHECK(0 < my_levelset(0, -0.01, 1.9)); + CHECK(Approx((1 - 0.5 * 1.9) * (1 - 0.5 * 1.9) - 0.01 * 0.01) == + my_levelset(0, -0.01, 1.9)); + + CHECK(0 < my_levelset(0, 0.4, 1.01)); + CHECK(Approx((1 - 0.5 * 1.01) * (1 - 0.5 * 1.01) - 0.4 * 0.4) == + my_levelset(0, 0.4, 1.01)); + + CHECK(0 < my_levelset(-0.4, 0, 1.1)); + CHECK(Approx((1 - 0.5 * 1.1) * (1 - 0.5 * 1.1) - 0.4 * 0.4) == + my_levelset(-0.4, 0, 1.1)); + } + } + } + } +} + +} // namespace diff --git a/src/csg/tests/levelset/transform.t.cpp b/src/csg/tests/levelset/transform.t.cpp new file mode 100644 index 0000000..055bf4f --- /dev/null +++ b/src/csg/tests/levelset/transform.t.cpp @@ -0,0 +1,140 @@ +#include + +#include "catch2/catch.hpp" + +#include +#include + +namespace { + +TEST_CASE("Translation", "[Levelset Transform]") { + + csg::Cylinder my_cyl{.radius = 2, .height = 20, .center = true}; + + auto my_mat = csg::Mulmatrix(); + my_mat.rotation[0] = std::array{{1, 0, 0}}; + my_mat.rotation[1] = std::array{{0, 1, 0}}; + my_mat.rotation[2] = std::array{{0, 0, 1}}; + my_mat.translation = {100, 200, 300}; + my_mat.group.objs.push_back(my_cyl); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_mat); + csg::CsgIF my_levelset(my_tree); + + SECTION("Outside") { + CHECK_FALSE(0 < my_levelset(100, 200, 280)); + CHECK_FALSE(0 < my_levelset(100, 200, 320)); + CHECK_FALSE(0 < my_levelset(103, 200, 300)); + CHECK_FALSE(0 < my_levelset(97, 200, 300)); + CHECK_FALSE(0 < my_levelset(100, 203, 300)); + CHECK_FALSE(0 < my_levelset(100, 197, 300)); + } + + SECTION("Inside") { + CHECK(0 < my_levelset(100, 200, 295)); + CHECK(0 < my_levelset(100, 200, 305)); + CHECK(0 < my_levelset(101, 200, 300)); + CHECK(0 < my_levelset(99, 200, 300)); + CHECK(0 < my_levelset(100, 201, 300)); + CHECK(0 < my_levelset(100, 199, 300)); + } +} + +TEST_CASE("90° Rotation around y-axis, rotating z-axis into x-axis", + "[Levelset Transform]") { + + csg::Cylinder my_cyl{.radius = 2, .height = 20, .center = false}; + + auto my_mat = csg::Mulmatrix(); + my_mat.rotation[0] = std::array{{0, 0, 1}}; + my_mat.rotation[1] = std::array{{0, 1, 0}}; + my_mat.rotation[2] = std::array{{-1, 0, 0}}; + my_mat.translation = {0, 0, 0}; + my_mat.group.objs.push_back(my_cyl); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_mat); + csg::CsgIF my_levelset(my_tree); + + SECTION("near origin end") { + CHECK_FALSE(0 < my_levelset(-1, 0, 0)); + CHECK(0 < my_levelset(1, 0, 0)); + + CHECK_FALSE(0 < my_levelset(3, 0, 2.1)); + CHECK(0 < my_levelset(3, 0, 1.9)); + + CHECK_FALSE(0 < my_levelset(3, 0, -2.1)); + CHECK(0 < my_levelset(3, 0, -1.9)); + } + SECTION("near middle") { + CHECK_FALSE(0 < my_levelset(10, 2.1, 0)); + CHECK(0 < my_levelset(10, 1.9, 0)); + + CHECK_FALSE(0 < my_levelset(10, -2.1, 0)); + CHECK(0 < my_levelset(10, -1.9, 0)); + } + SECTION("near other end") { + CHECK_FALSE(0 < my_levelset(21, 0, 0)); + CHECK(0 < my_levelset(19, 0, 0)); + + CHECK_FALSE(0 < my_levelset(18, 0, 2.1)); + CHECK(0 < my_levelset(18, 0, 1.9)); + + CHECK_FALSE(0 < my_levelset(18, 0, -2.1)); + CHECK(0 < my_levelset(18, 0, -1.9)); + } +} + +TEST_CASE("Orthogonal rotation + translation of cylinder", + "[Levelset Transform]") { + + double radius = 4.5, height = 10; + double Cx = 10, Cy = 5, Cz = 5; + + csg::Cylinder my_cyl{.radius = radius, .height = height, .center = true}; + auto my_mat = csg::Mulmatrix(); + my_mat.rotation[0] = std::array{{0, 0, 1}}; + my_mat.rotation[1] = std::array{{0, 1, 0}}; + my_mat.rotation[2] = std::array{{-1, 0, 0}}; + my_mat.translation = {Cx, Cy, Cz}; + my_mat.group.objs.push_back(my_cyl); + + auto my_tree = std::make_shared(); + my_tree->top.objs.push_back(my_mat); + csg::CsgIF my_levelset(my_tree); + + SECTION("near origin end") { + CHECK_FALSE(0 < my_levelset(Cx - (1.1 * height / 2), Cy, Cz)); + CHECK(0 < my_levelset(Cx - (0.9 * height / 2), Cy, Cz)); + + CHECK_FALSE(0 < + my_levelset(Cx - (0.9 * height / 2), Cy, Cz + 1.1 * radius)); + CHECK(0 < my_levelset(Cx - (0.9 * height / 2), Cy, 0.9 * radius)); + + CHECK_FALSE(0 < + my_levelset(Cx - (0.9 * height / 2), Cy, Cz - 1.1 * radius)); + CHECK(0 < my_levelset(Cx - (0.9 * height / 2), Cy, Cz - 0.9 * radius)); + } + SECTION("near middle") { + CHECK_FALSE(0 < my_levelset(Cx, Cy + 1.1 * radius, Cz)); + CHECK(0 < my_levelset(Cx, Cy + 0.9 * radius, Cz)); + + CHECK_FALSE(0 < my_levelset(Cx, Cy - 1.1 * radius, Cz)); + CHECK(0 < my_levelset(Cx, Cy - 0.9 * radius, Cz)); + } + SECTION("near other end") { + CHECK_FALSE(0 < my_levelset(Cx + (1.1 * height / 2), Cy, Cz)); + CHECK(0 < my_levelset(Cx + (0.9 * height / 2), Cy, Cz)); + + CHECK_FALSE(0 < + my_levelset(Cx + (0.9 * height / 2), Cy, Cz + 1.1 * radius)); + CHECK(0 < my_levelset(Cx + (0.9 * height / 2), Cy, Cz + 0.9 * radius)); + + CHECK_FALSE(0 < + my_levelset(Cx + (0.9 * height / 2), Cy, Cz - 1.1 * radius)); + CHECK(0 < my_levelset(Cx + (0.9 * height / 2), Cy, Cz - 0.9 * radius)); + } +} + +} // namespace diff --git a/src/csg/tests/parser/boolean.t.cpp b/src/csg/tests/parser/boolean.t.cpp new file mode 100644 index 0000000..45fa062 --- /dev/null +++ b/src/csg/tests/parser/boolean.t.cpp @@ -0,0 +1,86 @@ +#include "catch2/catch.hpp" + +#include +#include + +// Tests for CSG union(), intersection(), and difference() + +TEST_CASE("two shapes", "[csg]") { + auto st = *csg::parse_csg(R"( +sphere(r = 10); +cube(size = [1,2,3], center=true); +)"); + auto sph = std::get(st.top.objs.at(0)); + CHECK(sph.radius == 10); + auto cub = std::get(st.top.objs.at(1)); + + auto [XX, YY, ZZ] = cub.size; + CHECK(XX == 1); + CHECK(YY == 2); + CHECK(ZZ == 3); +} + +TEST_CASE("union", "[csg]") { + auto st = *csg::parse_csg(R"( +union() { + cube(size = [12, 14, 15], center = true); + sphere(r = 8); +} +)"); + + auto un = std::get(st.top.objs.back()); + CHECK(st.top.objs.size() == 1); + + auto cub = std::get(un.objs.at(0)); + auto sph = std::get(un.objs.at(1)); + + auto [XX, YY, ZZ] = cub.size; + CHECK(XX == 12); + CHECK(YY == 14); + CHECK(ZZ == 15); + CHECK(sph.radius == 8); +} + +TEST_CASE("intersection", "[csg]") { + auto st = *csg::parse_csg(R"( +intersection() { + cube(size = [15, 15, 15], center = true); + sphere(r = 10); +} +)"); + + CHECK(st.top.objs.size() == 1); + auto ints = std::get(st.top.objs.back()); + CHECK(ints.objs.size() == 2); + + auto cub = std::get(ints.objs.at(0)); + + auto sph = std::get(ints.objs.at(1)); + + auto [XX, YY, ZZ] = cub.size; + CHECK(XX == 15); + CHECK(YY == 15); + CHECK(ZZ == 15); + CHECK(sph.radius == 10); +} + +TEST_CASE("difference", "[csg]") { + auto st = *csg::parse_csg(R"( +difference() { + cube(size = [12, 12, 12], center = true); + sphere(r = 8); +} +)"); + + auto diff = std::get(st.top.objs.back()); + CHECK(st.top.objs.size() == 1); + + auto cub = std::get(*diff.first_obj); + auto sph = std::get(diff.next_objs.objs.at(0)); + + auto [XX, YY, ZZ] = cub.size; + CHECK(XX == 12); + CHECK(YY == 12); + CHECK(ZZ == 12); + CHECK(sph.radius == 8); +} diff --git a/tests/main.cpp b/src/csg/tests/parser/main.cpp similarity index 100% rename from tests/main.cpp rename to src/csg/tests/parser/main.cpp diff --git a/src/csg/tests/parser/nest.cpp b/src/csg/tests/parser/nest.cpp new file mode 100644 index 0000000..60a28a3 --- /dev/null +++ b/src/csg/tests/parser/nest.cpp @@ -0,0 +1,35 @@ +#include "catch2/catch.hpp" + +#include +#include + +TEST_CASE("matmul in matmul", "[csg]") { + auto maybe_st = csg::parse_csg(R"( +multmatrix( +[ +[1, 0, 0, 1], +[0, 1, 0, 2], +[0, 0, 1, 3], +[0, 0, 0, 1] +] +) { + cylinder(h = 2, r = 10, center=true); +multmatrix( +[ +[1, 0, 0, 4], +[0, 1, 0, 5], +[0, 0, 1, 6], +[0, 0, 0, 1] +] +) { + cylinder(h = 2, r = 10, center=true); +}} +)"); + CHECK(maybe_st != nullptr); + auto st = maybe_st.get(); + auto mat = std::get(st->top.objs.back()); + auto mat2 = std::get(mat.group.objs.back()); + auto cyl = std::get(mat2.group.objs.at(0)); + CHECK(cyl.height == 2); + CHECK(cyl.radius == 10); +} diff --git a/src/csg/tests/parser/other.t.cpp b/src/csg/tests/parser/other.t.cpp new file mode 100644 index 0000000..fee4779 --- /dev/null +++ b/src/csg/tests/parser/other.t.cpp @@ -0,0 +1,49 @@ +#include "catch2/catch.hpp" + +#include +#include + +// Tests that don't fit elsewhere + +// group() same as union() + +TEST_CASE("group", "[csg]") { + CHECK(csg::parse_csg(R"( +group() { + cylinder($fn = 0, $fa = 5, $fs = 0.1, h = 20, r1 = 5, r2 = 5, center = true); +} +)")); +} + +// render() should be accepted, but ignored +TEST_CASE("render", "[csg]") { + CHECK(csg::parse_csg(R"( +render(convexity=2) { + cylinder($fn = 0, $fa = 5, $fs = 0.1, h = 20, r1 = 5, r2 = 5, center = true); +} +)")); +} + +// First Basic Example when running OpenSCAD + +TEST_CASE("OpenSCAD Basic Example", "[csg]") { + CHECK(csg::parse_csg(R"( +multmatrix([[1, 0, 0, -24], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + union() { + cube(size = [15, 15, 15], center = true); + sphere($fn = 0, $fa = 12, $fs = 2, r = 10); + } +} +intersection() { + cube(size = [15, 15, 15], center = true); + sphere($fn = 0, $fa = 12, $fs = 2, r = 10); +} +multmatrix([[1, 0, 0, 24], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + difference() { + cube(size = [15, 15, 15], center = true); + sphere(r = 10); + } +} +group(); +)")); +} diff --git a/src/csg/tests/parser/primitives.t.cpp b/src/csg/tests/parser/primitives.t.cpp new file mode 100644 index 0000000..03493ba --- /dev/null +++ b/src/csg/tests/parser/primitives.t.cpp @@ -0,0 +1,44 @@ +#include "catch2/catch.hpp" + +#include +#include + +// Tests for the primitives on their own + +TEST_CASE("cylinder", "[csg]") { + auto st = *csg::parse_csg(R"( +cylinder(h = 2, r = 10, center=true); +)"); + auto cyl = std::get(st.top.objs.at(0)); + CHECK(cyl.radius == 10); + CHECK(cyl.height == 2); +} + +TEST_CASE("cube", "[csg]") { + auto st = *csg::parse_csg(R"( +cube(size = [1,2,3], center=true); +)"); + auto cub = std::get(st.top.objs.at(0)); + auto [XX, YY, ZZ] = cub.size; + CHECK(XX == 1); + CHECK(YY == 2); + CHECK(ZZ == 3); +} + +TEST_CASE("sphere", "[csg]") { + auto st = *csg::parse_csg(R"( +sphere(r = 10); +)"); + auto sph = std::get(st.top.objs.at(0)); + CHECK(sph.radius == 10); +} + +TEST_CASE("semicolon optional", "[csg]") { + auto st = *csg::parse_csg(R"( +sphere(r = 10) +)"); + auto sph = std::get(st.top.objs.at(0)); + CHECK(sph.radius == 10); +} + +// TODO: polyhedron() diff --git a/src/csg/tests/parser/transform.t.cpp b/src/csg/tests/parser/transform.t.cpp new file mode 100644 index 0000000..d1fce41 --- /dev/null +++ b/src/csg/tests/parser/transform.t.cpp @@ -0,0 +1,133 @@ +#include "catch2/catch.hpp" + +#include +#include + +// color() should be accepted, but ignored +TEST_CASE("color", "[csg]") { + CHECK(csg::parse_csg(R"( +color([0, 1, 0, 1]) { + cylinder($fn = 0, $fa = 5, $fs = 0.1, h = 20, r1 = 5, r2 = 5, center = true); +} +)")); +} + +TEST_CASE("one shape in 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] +] +) { + cylinder(h = 2, r = 10, center=true); +} +)"); + auto mat = std::get(st.top.objs.back()); + CHECK(mat.group.objs.size() == 1); + auto cyl = std::get(mat.group.objs.at(0)); + CHECK(cyl.height == 2); + CHECK(cyl.radius == 10); +} + +TEST_CASE("two shapes in 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] +] +) { + cylinder(h = 2, r = 10, center=true); + sphere(r = 10); +} +)"); + auto mat = std::get(st.top.objs.back()); + auto cyl = std::get(mat.group.objs.at(0)); + auto sph = std::get(mat.group.objs.at(1)); + CHECK(cyl.height == 2); + CHECK(cyl.radius == 10); + CHECK(sph.radius == 10); +} + +TEST_CASE("two matmuls", "[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] +] +) { + cylinder(h = 2, r = 10, center=true); + sphere(r = 11); +} +multmatrix( +[ +[1, 0, 0, 0.0020], +[0, 1, 0, 0.0005], +[0, 0, 1, 0.0005], +[0, 0, 0, 1] +] +) { + cube(size = [1,2,3], center=true); + cylinder(h=4, r1=1, r2=2, center=true); +} +)"); + auto mat = std::get(st.top.objs.at(0)); + auto cyl = std::get(mat.group.objs.at(0)); + auto sph = std::get(mat.group.objs.at(1)); + CHECK(cyl.height == 2); + CHECK(cyl.radius == 10); + CHECK(sph.radius == 11); + + auto mat2 = std::get(st.top.objs.at(1)); + auto cube = std::get(mat2.group.objs.at(0)); + auto cone = std::get(mat2.group.objs.at(1)); + auto [XX, YY, ZZ] = cube.size; + CHECK(XX == 1); + CHECK(YY == 2); + CHECK(ZZ == 3); + CHECK(cone.height == 4); + CHECK(cone.radius1 == 1); + CHECK(cone.radius2 == 2); +} + +TEST_CASE("two shapes one matmul", "[csg]") { + auto st = *csg::parse_csg(R"( +cylinder(h = 2, r = 10, center=true); +sphere(r = 11); +multmatrix( +[ +[1, 0, 0, 0.0020], +[0, 1, 0, 0.0005], +[0, 0, 1, 0.0005], +[0, 0, 0, 1] +] +) { + cube(size = [1,2,3], center=true); + cylinder(h=4, r1=1, r2=2, center=true); +} +)"); + auto cyl = std::get(st.top.objs.at(0)); + auto sph = std::get(st.top.objs.at(1)); + auto mat = std::get(st.top.objs.at(2)); + CHECK(cyl.height == 2); + CHECK(cyl.radius == 10); + CHECK(sph.radius == 11); + + auto cube = std::get(mat.group.objs.at(0)); + auto cone = std::get(mat.group.objs.at(1)); + auto [XX, YY, ZZ] = cube.size; + CHECK(XX == 1); + CHECK(YY == 2); + CHECK(ZZ == 3); + CHECK(cone.height == 4); + CHECK(cone.radius1 == 1); + CHECK(cone.radius2 == 2); +} diff --git a/src/CMakeLists.txt b/src/inputs/CMakeLists.txt similarity index 100% rename from src/CMakeLists.txt rename to src/inputs/CMakeLists.txt diff --git a/src/geometry.cpp b/src/inputs/geometry.cpp similarity index 100% rename from src/geometry.cpp rename to src/inputs/geometry.cpp diff --git a/src/inputs.hpp b/src/inputs/inputs.hpp similarity index 100% rename from src/inputs.hpp rename to src/inputs/inputs.hpp diff --git a/src/mesh.cpp b/src/inputs/mesh.cpp similarity index 100% rename from src/mesh.cpp rename to src/inputs/mesh.cpp diff --git a/src/inputs/meson.build b/src/inputs/meson.build new file mode 100644 index 0000000..a435c0a --- /dev/null +++ b/src/inputs/meson.build @@ -0,0 +1,11 @@ +lib_inputs_parser = static_library( + 'inputs-parser', + 'geometry.cpp', + 'mesh.cpp', + 'parser.cpp', + 'solver.cpp', + 'time.cpp', + include_directories: tao_inc, + install : true) + +subdir('tests') diff --git a/src/parser.cpp b/src/inputs/parser.cpp similarity index 100% rename from src/parser.cpp rename to src/inputs/parser.cpp diff --git a/src/solver.cpp b/src/inputs/solver.cpp similarity index 100% rename from src/solver.cpp rename to src/inputs/solver.cpp diff --git a/src/solver.hpp b/src/inputs/solver.hpp similarity index 100% rename from src/solver.hpp rename to src/inputs/solver.hpp diff --git a/tests/CMakeLists.txt b/src/inputs/tests/CMakeLists.txt similarity index 100% rename from tests/CMakeLists.txt rename to src/inputs/tests/CMakeLists.txt diff --git a/src/inputs/tests/main.cpp b/src/inputs/tests/main.cpp new file mode 100644 index 0000000..fc72584 --- /dev/null +++ b/src/inputs/tests/main.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN // Catch main() - only add to one cpp file +#include "catch2/catch.hpp" diff --git a/src/inputs/tests/meson.build b/src/inputs/tests/meson.build new file mode 100644 index 0000000..74f0aa9 --- /dev/null +++ b/src/inputs/tests/meson.build @@ -0,0 +1,9 @@ +unit_exe = executable( + 'unit_tests', + 'parser.t.cpp', + 'solver.t.cpp', + 'main.cpp', + include_directories: [parser_inc, catch2_inc], + link_with: lib_inputs_parser +) +test('unit_test', unit_exe) diff --git a/tests/parser.t.cpp b/src/inputs/tests/parser.t.cpp similarity index 100% rename from tests/parser.t.cpp rename to src/inputs/tests/parser.t.cpp diff --git a/tests/solver.t.cpp b/src/inputs/tests/solver.t.cpp similarity index 100% rename from tests/solver.t.cpp rename to src/inputs/tests/solver.t.cpp diff --git a/src/time.cpp b/src/inputs/time.cpp similarity index 100% rename from src/time.cpp rename to src/inputs/time.cpp diff --git a/src/meson.build b/src/meson.build deleted file mode 100644 index abe80e4..0000000 --- a/src/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -lib_parser = static_library('mfix-parser', - 'geometry.cpp', - 'mesh.cpp', - 'parser.cpp', - 'solver.cpp', - 'time.cpp', - include_directories: tao_inc, - install : true) - -executable('mfix-parser', - 'main.cpp', - link_with: lib_parser) diff --git a/tests/meson.build b/tests/meson.build deleted file mode 100644 index 2b302c3..0000000 --- a/tests/meson.build +++ /dev/null @@ -1,8 +0,0 @@ -unit_exe = executable('unit_tests', - 'parser.t.cpp', - 'solver.t.cpp', - 'main.cpp', - include_directories: [parser_inc, catch2_inc], - link_with: lib_parser - ) -test('unit_test', unit_exe) -- GitLab From 91db3ddb35cc8509b3441c0df2c6f705717bf3d9 Mon Sep 17 00:00:00 2001 From: Mark Meredith Date: Fri, 24 Apr 2020 10:47:04 -0400 Subject: [PATCH 2/6] Cleanup --- CMakeLists.txt | 41 -------------------------- src/csg/CMakeLists.txt | 1 - src/csg/tests/CMakeLists.txt | 51 --------------------------------- src/inputs/CMakeLists.txt | 13 --------- src/inputs/tests/CMakeLists.txt | 46 ----------------------------- 5 files changed, 152 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 src/inputs/CMakeLists.txt delete mode 100644 src/inputs/tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 8f49098..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -project(MFIX-Parser - DESCRIPTION "A parser for MFIX-Exa input files" - HOMEPAGE_URL "https://mfix.netl.doe.gov/gitlab/exa/mfix-parser" - LANGUAGES CXX - ) - -set( CMAKE_EXPORT_COMPILE_COMMANDS ON ) -set(USE_CCACHE "") -find_program(CCACHE_FOUND ccache) -if(CCACHE_FOUND) - set( CMAKE_CXX_COMPILER_LAUNCHER ccache ) -endif() - -add_subdirectory(src/csg) -add_subdirectory(src/inputs) -add_subdirectory(tests) - -find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang-format) - -if(CLANG_FMT) - get_target_property(_psrcs unit_tests SOURCES) - add_custom_target(fmt_test - COMMAND ${CLANG_FMT} -i ${_psrcs} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests - ) - file(GLOB _includes ${PROJECT_SOURCE_DIR}/src/csg/*.hpp) - file(GLOB _includes ${PROJECT_SOURCE_DIR}/src/inputs/*.hpp) - - add_custom_target(fmt) - add_dependencies(fmt fmt_test) -endif() - -set(EXENAME "mfix-parser") -add_executable(${EXENAME} src/main.cpp) -target_link_libraries(${EXENAME} parser) diff --git a/src/csg/CMakeLists.txt b/src/csg/CMakeLists.txt index c6c500e..8bf1280 100644 --- a/src/csg/CMakeLists.txt +++ b/src/csg/CMakeLists.txt @@ -22,7 +22,6 @@ find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang- if(CLANG_FMT) get_target_property(_ut_srcs unit_tests SOURCES) - get_target_property(_ft_srcs functional_tests SOURCES) get_target_property(_csg_srcs csg SOURCES) file(GLOB _csg_incs ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/impl/*.hpp) diff --git a/src/csg/tests/CMakeLists.txt b/src/csg/tests/CMakeLists.txt index 87f5205..e4e8b93 100644 --- a/src/csg/tests/CMakeLists.txt +++ b/src/csg/tests/CMakeLists.txt @@ -14,65 +14,16 @@ add_executable(unit_tests EXCLUDE_FROM_ALL parser/transform.t.cpp ) -add_executable(functional_tests EXCLUDE_FROM_ALL - benchmarks/05-cyl-fluidbed.cpp - benchmarks/07-hopper.cpp - benchmarks/08-cyclone.cpp - benchmarks/02-settling.cpp - benchmarks/tutorial-clr-prototype.cpp - benchmarks/main.cpp - ) - target_include_directories(unit_tests PRIVATE ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include ${CMAKE_SOURCE_DIR}/src/eb/csg ${CMAKE_SOURCE_DIR}/src/eb/csg/impl ) -target_include_directories(functional_tests PRIVATE - ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include - ${CMAKE_SOURCE_DIR}/src/eb/csg - amrex - ) target_compile_features(unit_tests PRIVATE cxx_std_17) -# Special treatment for CUDA in functional_tests -if(ENABLE_CUDA) - target_compile_definitions(functional_tests PRIVATE CSG_USE_GPU) - get_target_property(_src_cpp functional_tests SOURCES) - list(FILTER _src_cpp INCLUDE REGEX "\.cpp") - set_source_files_properties(${_src_cpp} PROPERTIES LANGUAGE CUDA ) - set_target_properties( functional_tests - PROPERTIES - CUDA_SEPARABLE_COMPILATION ON # This add -dc flag - ) -else() - target_compile_features(functional_tests PRIVATE cxx_std_17) -endif() - -get_filename_component(TUTORIAL_CLR_CSG - "${PROJECT_SOURCE_DIR}/tutorials/clr/prototype/clr.csg" - ABSOLUTE) -target_compile_definitions(functional_tests - PRIVATE TUTORIAL_CLR_CSG="${TUTORIAL_CLR_CSG}") - SET(catch2_dir ${PROJECT_SOURCE_DIR}/subprojects/Catch2) -find_package(Git QUIET) -if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Update submodules as needed - option(GIT_SUBMODULE "Check submodules during build" ON) - if(GIT_SUBMODULE) - message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") - endif() - endif() -endif() - if(NOT EXISTS "${catch2_dir}/CMakeLists.txt") message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") endif() @@ -81,9 +32,7 @@ add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build) list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/") target_link_libraries(unit_tests Catch2::Catch2 csg) -target_link_libraries(functional_tests Catch2::Catch2 AMReX::amrex csg) include(CTest) include(Catch) catch_discover_tests(unit_tests) -catch_discover_tests(functional_tests) diff --git a/src/inputs/CMakeLists.txt b/src/inputs/CMakeLists.txt deleted file mode 100644 index b83493c..0000000 --- a/src/inputs/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -################################################################################ -# inputs Parser -################################################################################ - -add_library(parser - parser.cpp - solver.cpp - ) - -target_include_directories(parser PRIVATE - ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include) - -target_link_libraries(parser PRIVATE stdc++fs) diff --git a/src/inputs/tests/CMakeLists.txt b/src/inputs/tests/CMakeLists.txt deleted file mode 100644 index ddb31a9..0000000 --- a/src/inputs/tests/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -################################################################################ -# Parser Tests -################################################################################ - -add_executable(unit_tests EXCLUDE_FROM_ALL - inputs.t.cpp - parser.t.cpp - main.cpp - solver.t.cpp - ) -target_link_libraries(unit_tests parser) - -target_include_directories(unit_tests PRIVATE - ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include - ${CMAKE_SOURCE_DIR}/src - ) - -SET(catch2_dir ${PROJECT_SOURCE_DIR}/subprojects/Catch2) - -find_package(Git QUIET) -if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Update submodules as needed - option(GIT_SUBMODULE "Check submodules during build" ON) - if(GIT_SUBMODULE) - message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") - endif() - endif() -endif() - -if(NOT EXISTS "${catch2_dir}/CMakeLists.txt") - message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") -endif() - -add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build) -list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/") - -target_link_libraries(unit_tests Catch2::Catch2) - -include(CTest) -include(Catch) -catch_discover_tests(unit_tests) -- GitLab From 22a405fac450e4adba3b6e290c65ce08b975740f Mon Sep 17 00:00:00 2001 From: Mark Meredith Date: Fri, 24 Apr 2020 11:03:30 -0400 Subject: [PATCH 3/6] more cleanup --- src/csg/CMakeLists.txt | 1 - src/csg/meson.build | 2 ++ src/csg/tests/CMakeLists.txt | 23 ----------------------- src/csg/tests/meson.build | 18 ++++++++++++++++++ 4 files changed, 20 insertions(+), 24 deletions(-) create mode 100644 src/csg/tests/meson.build diff --git a/src/csg/CMakeLists.txt b/src/csg/CMakeLists.txt index 8bf1280..a0d27bc 100644 --- a/src/csg/CMakeLists.txt +++ b/src/csg/CMakeLists.txt @@ -21,7 +21,6 @@ add_subdirectory(tests) find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang-format) if(CLANG_FMT) - get_target_property(_ut_srcs unit_tests SOURCES) get_target_property(_csg_srcs csg SOURCES) file(GLOB _csg_incs ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/impl/*.hpp) diff --git a/src/csg/meson.build b/src/csg/meson.build index 7c9fe91..ca6bcd5 100644 --- a/src/csg/meson.build +++ b/src/csg/meson.build @@ -5,3 +5,5 @@ lib_csg_parser = static_library( 'impl/parser.cpp', include_directories: tao_inc, install : true) + +subdir('tests') diff --git a/src/csg/tests/CMakeLists.txt b/src/csg/tests/CMakeLists.txt index e4e8b93..0f857bf 100644 --- a/src/csg/tests/CMakeLists.txt +++ b/src/csg/tests/CMakeLists.txt @@ -2,26 +2,6 @@ # CSG Parser Tests ################################################################################ -add_executable(unit_tests EXCLUDE_FROM_ALL - levelset/boolean.t.cpp - levelset/primitives.t.cpp - levelset/transform.t.cpp - parser/boolean.t.cpp - parser/main.cpp - parser/nest.cpp - parser/other.t.cpp - parser/primitives.t.cpp - parser/transform.t.cpp - ) - -target_include_directories(unit_tests PRIVATE - ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include - ${CMAKE_SOURCE_DIR}/src/eb/csg - ${CMAKE_SOURCE_DIR}/src/eb/csg/impl - ) - -target_compile_features(unit_tests PRIVATE cxx_std_17) - SET(catch2_dir ${PROJECT_SOURCE_DIR}/subprojects/Catch2) if(NOT EXISTS "${catch2_dir}/CMakeLists.txt") @@ -31,8 +11,5 @@ endif() add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build) list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/") -target_link_libraries(unit_tests Catch2::Catch2 csg) - include(CTest) include(Catch) -catch_discover_tests(unit_tests) diff --git a/src/csg/tests/meson.build b/src/csg/tests/meson.build new file mode 100644 index 0000000..3bb6d29 --- /dev/null +++ b/src/csg/tests/meson.build @@ -0,0 +1,18 @@ +unit_exe = executable( + 'unit_tests', + + 'levelset/boolean.t.cpp', + 'levelset/primitives.t.cpp', + 'levelset/transform.t.cpp', + 'parser/boolean.t.cpp', + 'parser/main.cpp', + 'parser/nest.cpp', + 'parser/other.t.cpp', + 'parser/primitives.t.cpp', + 'parser/transform.t.cpp', + + 'main.cpp', + include_directories: [parser_inc, catch2_inc], + link_with: lib_inputs_parser +) +test('unit_test', unit_exe) -- GitLab From b02442cc0590d580df6ac75553c3d762b6c5b323 Mon Sep 17 00:00:00 2001 From: Mark Meredith Date: Fri, 24 Apr 2020 11:14:33 -0400 Subject: [PATCH 4/6] Move to top --- src/csg/CMakeLists.txt => CMakeLists.txt | 19 +++++++++++++++---- src/csg/tests/CMakeLists.txt | 15 --------------- 2 files changed, 15 insertions(+), 19 deletions(-) rename src/csg/CMakeLists.txt => CMakeLists.txt (64%) delete mode 100644 src/csg/tests/CMakeLists.txt diff --git a/src/csg/CMakeLists.txt b/CMakeLists.txt similarity index 64% rename from src/csg/CMakeLists.txt rename to CMakeLists.txt index a0d27bc..a76da6b 100644 --- a/src/csg/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,9 @@ add_library(csg) target_sources(csg PRIVATE - impl/parser.cpp - impl/levelset.cpp - impl/csg.cpp + src/csg/impl/parser.cpp + src/csg/impl/levelset.cpp + src/csg/impl/csg.cpp ) target_include_directories(csg PRIVATE @@ -16,7 +16,18 @@ target_link_libraries(csg PRIVATE stdc++fs) target_compile_features(csg PRIVATE cxx_std_17) -add_subdirectory(tests) +SET(catch2_dir ${CMAKE_CURRENT_SOURCE_DIR}/subprojects/Catch2) + +if(NOT EXISTS "${catch2_dir}/CMakeLists.txt") + message(FATAL_ERROR "File ${catch2_dir}/CMakeLists.txt does not exist! The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") +endif() + +add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build) +list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/") + +include(CTest) +include(Catch) + find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang-format) diff --git a/src/csg/tests/CMakeLists.txt b/src/csg/tests/CMakeLists.txt deleted file mode 100644 index 0f857bf..0000000 --- a/src/csg/tests/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -################################################################################ -# CSG Parser Tests -################################################################################ - -SET(catch2_dir ${PROJECT_SOURCE_DIR}/subprojects/Catch2) - -if(NOT EXISTS "${catch2_dir}/CMakeLists.txt") - message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") -endif() - -add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build) -list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/") - -include(CTest) -include(Catch) -- GitLab From ade53e3a593b348dc7b16d5c75525d022001ed46 Mon Sep 17 00:00:00 2001 From: Mark Meredith Date: Fri, 24 Apr 2020 11:16:51 -0400 Subject: [PATCH 5/6] Fix include --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a76da6b..953f6ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ target_sources(csg PRIVATE ) target_include_directories(csg PRIVATE - ${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include) + ${CMAKE_CURRENT_SOURCE_DIR}/subprojects/PEGTL/include) target_link_libraries(csg PRIVATE stdc++fs) -- GitLab From ad147bee130d5dcc0b0464aa5e05c7f052129261 Mon Sep 17 00:00:00 2001 From: Mark Meredith Date: Fri, 24 Apr 2020 11:31:50 -0400 Subject: [PATCH 6/6] Fix build --- meson.build | 1 + src/csg/tests/meson.build | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index b2915d6..7c6e889 100644 --- a/meson.build +++ b/meson.build @@ -6,6 +6,7 @@ tao_inc = include_directories('subprojects/PEGTL/include') catch2_inc = include_directories('subprojects/Catch2/single_include') parser_inc = include_directories( 'src/csg', + 'src/csg/impl', 'src/inputs') subdir('src/csg') diff --git a/src/csg/tests/meson.build b/src/csg/tests/meson.build index 3bb6d29..4f06814 100644 --- a/src/csg/tests/meson.build +++ b/src/csg/tests/meson.build @@ -1,6 +1,5 @@ unit_exe = executable( 'unit_tests', - 'levelset/boolean.t.cpp', 'levelset/primitives.t.cpp', 'levelset/transform.t.cpp', @@ -10,9 +9,7 @@ unit_exe = executable( 'parser/other.t.cpp', 'parser/primitives.t.cpp', 'parser/transform.t.cpp', - - 'main.cpp', include_directories: [parser_inc, catch2_inc], - link_with: lib_inputs_parser + link_with: lib_csg_parser ) test('unit_test', unit_exe) -- GitLab