From af511efbe25ae25ccad65e9d9238ab44f1201ebe Mon Sep 17 00:00:00 2001
From: Deepak Rangarajan <deepak.rangarajan@netl.doe.gov>
Date: Mon, 15 Nov 2021 14:45:04 +0000
Subject: [PATCH] support cmake option to be able to build without CGAL and its
 dependencies. it...

---
 .gitlab-ci.yml                          | 19 +++++++++--------
 CMakeLists.txt                          |  8 +++++++-
 include/csg_types.hpp                   | 27 +++++++++++++++++++++++++
 src/csg/CMakeLists.txt                  | 19 +++++++++++++----
 src/csg/levelset_2d.cpp                 |  6 ++++++
 src/csg/levelset_3d.cpp                 | 10 +++++++++
 src/csg/parser.cpp                      | 16 +++++++++++++++
 src/csg/tests/CMakeLists.txt            | 21 +++++++++++++++----
 src/csg/tests/levelset/extrude.t.cpp    |  2 ++
 src/csg/tests/levelset/primitives.t.cpp |  2 ++
 src/csg/tests/parser/extrude.t.cpp      |  4 ++++
 src/csg/tests/parser/primitives.t.cpp   | 18 +++++++++++++++++
 12 files changed, 135 insertions(+), 17 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4442a89..3c96047 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,7 +16,6 @@ build-img:
   tags:
     - docker
 
-
 ###### Test jobs #####
 
 .test:cmake: &cmake_def
@@ -25,23 +24,27 @@ build-img:
   needs: ['build-img']
   script:
     - conan install -if build -g cmake_find_package catch2/2.13.7@
-    - conan install -if build -g cmake_find_package cgal/5.2.1@
+    - if [ "$ENABLE_CSG" == "ON" ]; then conan install -if build -g cmake_find_package cgal/5.2.1@; fi
     - conan install -if build -g cmake_find_package taocpp-pegtl/3.2.1@
 
-    - cmake -S. -Bbuild -GNinja -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_MODULE_PATH=$PWD/build
+    - cmake -S.
+            -Bbuild
+            -GNinja
+            -DCMAKE_MODULE_PATH=$PWD/build
+            -DCMAKE_BUILD_TYPE=Debug
+            -DCSG_CGAL_ENABLED=${ENABLE_CSG}
     - cmake --build build --target unit_tests_csg
     - cd build
     - ctest
   tags:
     - docker
 
-test:cmake:debug:
+test:cgal:
   variables:
-    BUILD_TYPE: "Debug"
+    ENABLE_CSG: "ON"
   <<: *cmake_def
 
-test:cmake:release:
+test:no_cgal:
   variables:
-    BUILD_TYPE: "Release"
-  allow_failure: true
+    ENABLE_CSG: "OFF"
   <<: *cmake_def
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 38703b8..042cbeb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,13 +12,19 @@ set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
 
+option(CSG_CGAL_ENABLED   "Enable CGAL"   ON)
+
 find_program(CCACHE_FOUND ccache)
 if(CCACHE_FOUND)
   set( CMAKE_CXX_COMPILER_LAUNCHER ccache )
 endif()
 
 find_package(pegtl REQUIRED)
-find_package(CGAL REQUIRED)
+
+if (CSG_CGAL_ENABLED)
+   find_package(CGAL REQUIRED)
+   add_definitions(-DUSE_CGAL)
+endif()
 
 add_subdirectory(src/csg)
 
diff --git a/include/csg_types.hpp b/include/csg_types.hpp
index 536c1f4..7f85146 100644
--- a/include/csg_types.hpp
+++ b/include/csg_types.hpp
@@ -1,12 +1,15 @@
 #ifndef CSG_TYPES_H_
 #define CSG_TYPES_H_
 
+#if USE_CGAL
 #include "csg_cgal_helper.hpp"
+#endif
 #include "csg_matrix_functions.hpp"
 
 #include <array>
 #include <memory>
 #include <optional>
+#include <string>
 #include <tuple>
 #include <variant>
 #include <vector>
@@ -50,6 +53,7 @@ struct Cone {
   bool center;
 };
 
+#if USE_CGAL
 struct Polyhedron {
 private:
   std::vector<std::tuple<double, double, double>> m_points;
@@ -77,7 +81,9 @@ public:
     return m_cgal_aabb_tree;
   }
 };
+#endif
 
+#if USE_CGAL
 struct Polygon {
 private:
   std::vector<std::tuple<double, double>> m_points;
@@ -97,6 +103,7 @@ public:
 
   const cgal_helper::Polygon &cgal_polygon() const { return m_cgal_polygon; }
 };
+#endif
 
 enum Dimension { D2, D3 };
 
@@ -110,19 +117,37 @@ struct Hull;
 
 template <Dimension dim> struct TypeHelper;
 
+#if USE_CGAL
 template <> struct TypeHelper<Dimension::D2> {
   using Type =
       std::variant<Circle, Square, Union<Dimension::D2>,
                    Intersection<Dimension::D2>, Difference<Dimension::D2>,
                    Mulmatrix<Dimension::D2>, Polygon>;
 };
+#else
+template <> struct TypeHelper<Dimension::D2> {
+  using Type =
+      std::variant<Circle, Square, Union<Dimension::D2>,
+                   Intersection<Dimension::D2>, Difference<Dimension::D2>,
+                   Mulmatrix<Dimension::D2>>;
+};
+#endif
 
+#if USE_CGAL
 template <> struct TypeHelper<Dimension::D3> {
   using Type = std::variant<Sphere, Cube, Cylinder, Cone, Union<Dimension::D3>,
                             Intersection<Dimension::D3>,
                             Difference<Dimension::D3>, Mulmatrix<Dimension::D3>,
                             LinearExtrude, RotateExtrude, Polyhedron, Hull>;
 };
+#else
+template <> struct TypeHelper<Dimension::D3> {
+  using Type =
+      std::variant<Sphere, Cube, Cylinder, Cone, Union<Dimension::D3>,
+                   Intersection<Dimension::D3>, Difference<Dimension::D3>,
+                   Mulmatrix<Dimension::D3>, LinearExtrude, RotateExtrude>;
+};
+#endif
 
 template <Dimension dim> struct Union {
   std::vector<typename TypeHelper<dim>::Type> objs;
@@ -195,6 +220,7 @@ struct RotateExtrude {
   Union<Dimension::D2> group;
 };
 
+#if USE_CGAL
 struct Hull {
 private:
   std::shared_ptr<cgal_helper::Polyhedron> m_cgal_polyhedron;
@@ -222,6 +248,7 @@ public:
     return m_cgal_aabb_tree;
   }
 };
+#endif
 
 struct Tree {
   Union<Dimension::D3> top;
diff --git a/src/csg/CMakeLists.txt b/src/csg/CMakeLists.txt
index bcf3a9b..867d42d 100644
--- a/src/csg/CMakeLists.txt
+++ b/src/csg/CMakeLists.txt
@@ -1,9 +1,9 @@
 ################################################################################
 # CSG Parser
 ################################################################################
-add_library(csg-eb
-  cgal_helper_polygon.cpp
-  cgal_helper_polyhedron.cpp
+
+set(csg-eb_SOURCES)
+list(APPEND csg-eb_SOURCES
   csg.cpp
   levelset_2d.cpp
   levelset_3d.cpp
@@ -12,10 +12,21 @@ add_library(csg-eb
   exception.cpp
   )
 
+if (CSG_CGAL_ENABLED)
+   list(APPEND csg-eb_SOURCES
+      cgal_helper_polygon.cpp
+      cgal_helper_polyhedron.cpp
+   )
+endif()
+
+add_library(csg-eb ${csg-eb_SOURCES})
+
 target_link_libraries(csg-eb PRIVATE
-                      CGAL::CGAL
                       stdc++fs
                       taocpp::pegtl
                       )
+if (CSG_CGAL_ENABLED)
+   target_link_libraries(csg-eb PRIVATE CGAL::CGAL)
+endif()
 
 add_subdirectory(tests)
diff --git a/src/csg/levelset_2d.cpp b/src/csg/levelset_2d.cpp
index 66a5284..50c9fe0 100644
--- a/src/csg/levelset_2d.cpp
+++ b/src/csg/levelset_2d.cpp
@@ -1,4 +1,6 @@
+#if USE_CGAL
 #include "csg_cgal_helper.hpp"
+#endif
 #include "csg_types.hpp"
 
 #include <algorithm>
@@ -25,7 +27,9 @@ 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);
+#if USE_CGAL
 double signed_distance_2d(const Polygon &, double, double);
+#endif
 
 double signed_distance_2d(const Union2D &group, double xx, double yy) {
   auto sdist = -std::numeric_limits<double>::max();
@@ -90,6 +94,7 @@ double signed_distance_2d(const Circle &cir, double xx, double yy) {
   return EXTERNAL_FLOW * sign * dist;
 }
 
+#if USE_CGAL
 double signed_distance_2d(const Polygon &polygon, double xx, double yy) {
 
   bool inside = cgal_helper::inside(polygon.cgal_polygon(), xx, yy);
@@ -99,6 +104,7 @@ double signed_distance_2d(const Polygon &polygon, double xx, double yy) {
 
   return EXTERNAL_FLOW * sign * dist;
 }
+#endif
 
 double signed_distance_2d(const Type2D &obj, double xx, double yy) {
 
diff --git a/src/csg/levelset_3d.cpp b/src/csg/levelset_3d.cpp
index 589b092..cdc0811 100644
--- a/src/csg/levelset_3d.cpp
+++ b/src/csg/levelset_3d.cpp
@@ -1,7 +1,11 @@
+#if USE_CGAL
 #include "csg_cgal_helper.hpp"
+#endif
 #include "csg_types.hpp"
 
 #include <algorithm>
+#include <cassert>
+#include <cmath>
 #include <iostream>
 #include <limits>
 
@@ -29,8 +33,10 @@ double signed_distance_3d(const Sphere &, double, double, double);
 double signed_distance_3d(const Type3D &, double, double, double);
 double signed_distance_3d(const LinearExtrude &, double, double, double);
 double signed_distance_3d(const RotateExtrude &, double, double, double);
+#if USE_CGAL
 double signed_distance_3d(const Polyhedron &, double, double, double);
 double signed_distance_3d(const Hull &, double, double, double);
+#endif
 
 double signed_distance_2d(const Union2D &, double, double);
 
@@ -176,17 +182,21 @@ double signed_distance_3d(const RotateExtrude &rot_ext, double xx, double yy,
   return EXTERNAL_FLOW * std::max(sign_t * dist_t, sign_2d * dist_2d);
 }
 
+#if USE_CGAL
 double signed_distance_3d(const Polyhedron &polyhedron, double xx, double yy,
                           double zz) {
   // TODO: support signed distance instead of -1.0/1.0
   return cgal_helper::inside(polyhedron.cgal_aabb_tree(), xx, yy, zz) ? 1.0
                                                                       : -1.0;
 }
+#endif
 
+#if USE_CGAL
 double signed_distance_3d(const Hull &hull, double xx, double yy, double zz) {
   // TODO: support signed distance instead of -1.0/1.0
   return cgal_helper::inside(hull.cgal_aabb_tree(), xx, yy, zz) ? 1.0 : -1.0;
 }
+#endif
 
 double signed_distance_3d(const Type3D &obj, double xx, double yy, double zz) {
 
diff --git a/src/csg/parser.cpp b/src/csg/parser.cpp
index 20f0f0d..86fcf7c 100644
--- a/src/csg/parser.cpp
+++ b/src/csg/parser.cpp
@@ -1,5 +1,6 @@
 // standard includes
 #include <iostream>
+#include <map>
 #include <memory>
 #include <stdexcept>
 
@@ -515,6 +516,7 @@ template <> struct action<hull> {
   template <typename Input>
   static void apply(const Input &in, parser_state &st) {
     std::stringstream ss(in.string());
+#if USE_CGAL
     csg::Hull hull;
 
     // Only a very limited hull is currently supported
@@ -546,6 +548,10 @@ template <> struct action<hull> {
         "hull() support is limited to a multmatrix of a centered cube followed "
         "by a sphere; added for the CLR support";
     throw csg::Exception(except_src, except_msg);
+#else
+    throw csg::Exception("action<hull>",
+                         "Needs to be built with CGAL enabled!");
+#endif
   }
 };
 
@@ -656,6 +662,7 @@ template <> struct action<polygon> {
   template <typename Input>
   static void apply(const Input &in, parser_state &st) {
     std::stringstream ss(in.string());
+#if USE_CGAL
     auto &curr_attr = st.curr_attrs.back();
 
     auto points_raw =
@@ -690,6 +697,10 @@ template <> struct action<polygon> {
 
     st.current_2d_objs.back().push_back(diff);
     st.curr_attrs.pop_back();
+#else
+    throw csg::Exception("action<polygon>",
+                         "Needs to be built with CGAL enabled");
+#endif
   }
 };
 
@@ -697,6 +708,7 @@ template <> struct action<polyhedron> {
   template <typename Input>
   static void apply(const Input &in, parser_state &st) {
     std::stringstream ss(in.string());
+#if USE_CGAL
     auto &curr_attr = st.curr_attrs.back();
 
     auto points_raw =
@@ -715,6 +727,10 @@ template <> struct action<polyhedron> {
 
     st.current_3d_objs.back().push_back(polyh);
     st.curr_attrs.pop_back();
+#else
+    throw csg::Exception("action<polyhedron>",
+                         "Needs to be build with CGAL enabled!");
+#endif
   }
 };
 
diff --git a/src/csg/tests/CMakeLists.txt b/src/csg/tests/CMakeLists.txt
index 7041326..3f4d075 100644
--- a/src/csg/tests/CMakeLists.txt
+++ b/src/csg/tests/CMakeLists.txt
@@ -2,18 +2,17 @@
 # CSG Parsing Tests
 ################################################################################
 
-add_executable(unit_tests_csg EXCLUDE_FROM_ALL
+set(unit_tests_csg_SOURCES)
+list(APPEND unit_tests_csg_SOURCES
   levelset/boolean.t.cpp
   levelset/empty.t.cpp
   levelset/extrude.t.cpp
-  levelset/hull.t.cpp
   levelset/internal_flow.t.cpp
   levelset/primitives.t.cpp
   levelset/transform.t.cpp
   parser/boolean.t.cpp
   parser/empty.t.cpp
   parser/extrude.t.cpp
-  parser/hull.t.cpp
   parser/nest.cpp
   parser/other.t.cpp
   parser/primitives.t.cpp
@@ -21,6 +20,17 @@ add_executable(unit_tests_csg EXCLUDE_FROM_ALL
   unit_tests_csg.main.cpp
   )
 
+if (CSG_CGAL_ENABLED)
+   list(APPEND unit_tests_csg_SOURCES
+     levelset/hull.t.cpp
+     parser/hull.t.cpp
+   )
+endif()
+
+add_executable(unit_tests_csg EXCLUDE_FROM_ALL
+   ${unit_tests_csg_SOURCES} 
+  )
+
 target_include_directories(unit_tests_csg
   PRIVATE ${CMAKE_SOURCE_DIR}/src
   )
@@ -28,9 +38,12 @@ target_include_directories(unit_tests_csg
 find_package(Catch2 REQUIRED)
 include(Catch)
 target_link_libraries(unit_tests_csg
-  CGAL::CGAL
   Catch2::Catch2
   csg-eb
   )
 
+if (CSG_CGAL_ENABLED)
+   target_link_libraries(unit_tests_csg CGAL::CGAL)
+endif()
+
 catch_discover_tests(unit_tests_csg)
diff --git a/src/csg/tests/levelset/extrude.t.cpp b/src/csg/tests/levelset/extrude.t.cpp
index cc402c2..8706dd0 100644
--- a/src/csg/tests/levelset/extrude.t.cpp
+++ b/src/csg/tests/levelset/extrude.t.cpp
@@ -161,6 +161,7 @@ TEST_CASE("simple torus", "[Levelset Extrude]") {
   }
 }
 
+#if USE_CGAL
 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;
@@ -244,6 +245,7 @@ TEST_CASE("Linear extrude of a triangle with hole", "[Levelset Primitives]") {
     CHECK(Approx(ht / 2) == my_levelset(0.5 * thick, 0.7 * outer, 0.0));
   }
 }
+#endif
 
 TEST_CASE("sliced torus", "[Levelset Extrude]") {
   auto my_rot_ext = csg::RotateExtrude{.angle = 45, .group = csg::Union2D()};
diff --git a/src/csg/tests/levelset/primitives.t.cpp b/src/csg/tests/levelset/primitives.t.cpp
index b950f82..57690ec 100644
--- a/src/csg/tests/levelset/primitives.t.cpp
+++ b/src/csg/tests/levelset/primitives.t.cpp
@@ -545,6 +545,7 @@ TEST_CASE("Cone", "[Levelset Primitives]") {
   }
 }
 
+#if USE_CGAL
 TEST_CASE("Polyhedron", "[Levelset Primitives]") {
   double Lx = 10.0, Ly = 7.0, Lz = 5.0;
   // A 10 x 7 x 5 cuboid in positive quandrant formed using polyhedron
@@ -584,5 +585,6 @@ TEST_CASE("Polyhedron", "[Levelset Primitives]") {
     CHECK(0 < my_levelset(0.7 * Lx, 0.1 * Ly, 0.01 * Lz));
   }
 }
+#endif
 
 } // namespace
diff --git a/src/csg/tests/parser/extrude.t.cpp b/src/csg/tests/parser/extrude.t.cpp
index a13b474..c37fd72 100644
--- a/src/csg/tests/parser/extrude.t.cpp
+++ b/src/csg/tests/parser/extrude.t.cpp
@@ -162,6 +162,7 @@ linear_extrude(height = 10, center = false, scale = [5, 5]) {
   CHECK(st == nullptr);
 }
 
+#if USE_CGAL
 TEST_CASE("linear extrude polygon without paths", "[csg]") {
   auto st = *csg::parse_csg(R"(
 linear_extrude(
@@ -189,7 +190,9 @@ paths = undef);
   CHECK(diff.next_objs.objs.size() == 0);
   CHECK(outer.cgal_polygon().size() == 4);
 }
+#endif
 
+#if USE_CGAL
 TEST_CASE("linear extrude polygon with hole", "[csg]") {
   auto st = *csg::parse_csg(R"(
 linear_extrude(
@@ -218,5 +221,6 @@ paths = [[0, 1, 2], [3, 4, 5]]);
   CHECK(outer.cgal_polygon().size() == 3);
   CHECK(inner.cgal_polygon().size() == 3);
 }
+#endif
 
 } // namespace
diff --git a/src/csg/tests/parser/primitives.t.cpp b/src/csg/tests/parser/primitives.t.cpp
index 818b82d..bc57dd4 100644
--- a/src/csg/tests/parser/primitives.t.cpp
+++ b/src/csg/tests/parser/primitives.t.cpp
@@ -5,6 +5,7 @@
 
 // Tests for the primitives on their own
 
+namespace {
 TEST_CASE("cylinder", "[csg]") {
   auto st = *csg::parse_csg(R"(
 cylinder($name="my_cyl", h = 2, r = 10, center=true);
@@ -54,6 +55,7 @@ sphere(r = 10)
   CHECK(sph.radius == 10);
 }
 
+#if USE_CGAL
 TEST_CASE("square pyramid polyhedron", "[csg]") {
   auto st = *csg::parse_csg(R"(
 polyhedron(
@@ -66,7 +68,9 @@ $name="my_polyhedron");
   CHECK(polyh.cgal_polyhedron()->size_of_vertices() == 5);
   CHECK(polyh.cgal_polyhedron()->size_of_facets() == 6);
 }
+#endif
 
+#if USE_CGAL
 TEST_CASE("bad vs good polyhedron openscad example", "[csg]") {
   // https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solids#polyhedron
   CHECK_THROWS(csg::parse_csg(R"(
@@ -103,3 +107,17 @@ convexity = 1);
   auto polyh = std::get<csg::Polyhedron>(st.top.objs.at(0));
   CHECK(polyh.cgal_polyhedron()->size_of_vertices() == 12);
 }
+#endif
+
+#if !(USE_CGAL)
+TEST_CASE("without cgal test", "[csg]") {
+  CHECK_THROWS(*csg::parse_csg(R"(
+polyhedron(
+points = [[10, 10, 0], [10, -10, 0], [-10, -10, 0], [-10, 10, 0], [0, 0, 10]], 
+faces = [[0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4], [1, 0, 3], [2, 1, 3]], 
+$name="my_polyhedron");
+)"));
+}
+#endif
+
+} // namespace
-- 
GitLab