Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ _build/
*.manifest
*.spec

# Third party dependencies
.third_party/

# IDE
.vscode/
.idea/
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added Clipper2 to CMakeLists.txt

### Changed

* Mapping method crops the pattern mesh to the boundaries of the target mesh.

### Removed

* Removed all the modules from __init__.py

## [0.4.0] 2025-04-05

Expand Down
219 changes: 104 additions & 115 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,166 +1,155 @@
cmake_minimum_required(VERSION 3.15...3.26)

project(compas_libigl LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_BUILD_TYPE Release)

# =====================================================================
# Set this flag to ON for developing to reduce build time.
# Set this flag to OFF for publishing for file size reduction.
# =====================================================================
option(ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers for the build" ON)

# =====================================================================
# Set maximum heap size for MSVC
# =====================================================================
option(ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
option(MULTITHREADED_COMPILATION "Enable multi-threaded compilation (Ninja only)" ON)

if(MSVC)
set(CMAKE_GENERATOR_PLATFORM x64)
add_compile_options(/Zm1200)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Release' as none was specified.")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

# =====================================================================
# Build size reduction.
# =====================================================================

if (NOT ENABLE_PRECOMPILED_HEADERS)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O1") # Optimize for size on MSVC
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os") # Optimize for size on GCC/Clang
if(MULTITHREADED_COMPILATION)
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
message(STATUS "Using ${N} build jobs.")
set(CMAKE_PARALLEL_LEVEL ${N})
if(CMAKE_GENERATOR MATCHES "^Ninja")
set(CMAKE_JOB_POOL_COMPILE compile)
set(CMAKE_JOB_POOL_LINK link)
set(CMAKE_JOB_POOLS "compile=${N}" "link=2")
endif()
endif()
endif()

# =====================================================================
# Dependencies
# =====================================================================
include(ExternalProject)

# Define source directories for external dependencies
set(EXTERNAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external")
set(EIGEN_SOURCE_DIR "${EXTERNAL_DIR}/eigen")
set(LIBIGL_SOURCE_DIR "${EXTERNAL_DIR}/libigl")

# Create directories if they don't exist
file(MAKE_DIRECTORY ${EXTERNAL_DIR})
file(MAKE_DIRECTORY ${EIGEN_SOURCE_DIR})
file(MAKE_DIRECTORY ${LIBIGL_SOURCE_DIR})

# Download Eigen first
if(NOT EXISTS "${EIGEN_SOURCE_DIR}/Eigen")
message(STATUS "Downloading Eigen...")
ExternalProject_Add(
eigen_download
PREFIX ${EXTERNAL_DIR}
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz
SOURCE_DIR "${EIGEN_SOURCE_DIR}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
UPDATE_COMMAND ""
PATCH_COMMAND ""
)
if(UNIX AND NOT APPLE)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}" CACHE PATH "Install prefix" FORCE)
endif()
endif()

# Download libigl after Eigen
if(NOT EXISTS "${LIBIGL_SOURCE_DIR}/include/igl")
message(STATUS "Downloading libigl...")
ExternalProject_Add(
libigl_download
DEPENDS eigen_download
PREFIX ${EXTERNAL_DIR}
URL https://github.com/libigl/libigl/archive/refs/heads/main.zip
SOURCE_DIR "${LIBIGL_SOURCE_DIR}"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
UPDATE_COMMAND ""
PATCH_COMMAND ""
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
# Use custom directory for 3rd-party dependencies (optional but persistent)
set(FETCHCONTENT_BASE_DIR "${CMAKE_SOURCE_DIR}/.third_party")
set(FETCHCONTENT_QUIET OFF)
set(FETCHCONTENT_UPDATES_DISCONNECTED ON)

include(FetchContent)

# === Eigen ===
# Check if Eigen already exists
set(EIGEN_DIR "${FETCHCONTENT_BASE_DIR}/eigen-src")
if(EXISTS "${EIGEN_DIR}")
message(STATUS "Using existing Eigen from ${EIGEN_DIR}")
set(eigen_SOURCE_DIR "${EIGEN_DIR}")
set(eigen_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/eigen-build")
endif()

# Create a custom target for all external dependencies
add_custom_target(external_downloads ALL)
if(TARGET eigen_download)
add_dependencies(external_downloads eigen_download)
FetchContent_Declare(
eigen
URL https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz
SOURCE_DIR "${eigen_SOURCE_DIR}"
BINARY_DIR "${eigen_BINARY_DIR}"
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(eigen)

# === libigl ===
# Check if libigl already exists
set(LIBIGL_DIR "${FETCHCONTENT_BASE_DIR}/libigl-src")
if(EXISTS "${LIBIGL_DIR}")
message(STATUS "Using existing libigl from ${LIBIGL_DIR}")
set(libigl_SOURCE_DIR "${LIBIGL_DIR}")
set(libigl_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/libigl-build")
endif()
if(TARGET libigl_download)
add_dependencies(external_downloads libigl_download)

FetchContent_Declare(
libigl
GIT_REPOSITORY https://github.com/libigl/libigl.git
GIT_TAG main
SOURCE_DIR "${libigl_SOURCE_DIR}"
BINARY_DIR "${libigl_BINARY_DIR}"
)
FetchContent_MakeAvailable(libigl)

# === Clipper2 ===
# Check if Clipper2 already exists
set(CLIPPER2_DIR "${FETCHCONTENT_BASE_DIR}/clipper2-src")
if(EXISTS "${CLIPPER2_DIR}")
message(STATUS "Using existing Clipper2 from ${CLIPPER2_DIR}")
set(clipper2_SOURCE_DIR "${CLIPPER2_DIR}")
set(clipper2_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/clipper2-build")
endif()

# Add include directories for external dependencies
set(EIGEN_INCLUDE_DIR "${EIGEN_SOURCE_DIR}")
set(LIBIGL_INCLUDE_DIR "${LIBIGL_SOURCE_DIR}/include")

if (NOT SKBUILD)
message(WARNING "\
This CMake file is meant to be executed using 'scikit-build'. Running
it directly will almost certainly not produce the desired result. If
you are a user trying to install this package, please use the command
below, which will install all necessary build dependencies, compile
the package in an isolated environment, and then install it.
=====================================================================
$ pip install .
=====================================================================
If you are a software developer, and this is your own package, then
it is usually much more efficient to install the build dependencies
in your environment once and use the following command that avoids
a costly creation of a new virtual environment at every compilation:
=====================================================================
$ pip install nanobind scikit-build-core[pyproject]
$ pip install --no-build-isolation -ve .
=====================================================================
You may optionally add -Ceditable.rebuild=true to auto-rebuild when
the package is imported. Otherwise, you need to re-run the above
after editing C++ files.")
FetchContent_Declare(
clipper2
URL https://github.com/AngusJohnson/Clipper2/releases/download/Clipper2_1.5.3/Clipper2_1.5.3.zip
SOURCE_DIR "${clipper2_SOURCE_DIR}"
BINARY_DIR "${clipper2_BINARY_DIR}"
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(clipper2)

# Build Clipper2 as a static library
file(GLOB CLIPPER2_SRC "${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/src/*.cpp")

add_library(clipper2_static STATIC ${CLIPPER2_SRC})
target_include_directories(clipper2_static PUBLIC "${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/include")
set_target_properties(clipper2_static PROPERTIES POSITION_INDEPENDENT_CODE ON)

# Install clipper2 static lib and headers if not already installed
set(CLIPPER2_HEADERS_DEST "${CMAKE_INSTALL_PREFIX}/include/clipper2")
if(NOT EXISTS "${CLIPPER2_HEADERS_DEST}")
message(STATUS "Installing Clipper2 headers and library")
install(TARGETS clipper2_static ARCHIVE DESTINATION lib)
install(DIRECTORY "${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/include/clipper2" DESTINATION include)
endif()

# Find Python and nanobind
find_package(Python 3.8
REQUIRED COMPONENTS Interpreter Development.Module
OPTIONAL_COMPONENTS Development.SABIModule)
# === Include paths ===
set(EIGEN_INCLUDE_DIR ${eigen_SOURCE_DIR})
set(LIBIGL_INCLUDE_DIR ${libigl_SOURCE_DIR}/include)
set(CLIPPER2_INCLUDE_DIR ${clipper2_SOURCE_DIR}/CPP/Clipper2Lib/include)

# === Python & nanobind ===
find_package(Python 3.8 REQUIRED COMPONENTS Interpreter Development.Module OPTIONAL_COMPONENTS Development.SABIModule)
find_package(nanobind CONFIG REQUIRED)
find_package(Threads REQUIRED)

# Create a shared precompiled header library
# === Precompiled headers ===
if (ENABLE_PRECOMPILED_HEADERS)
add_library(compas_pch INTERFACE)
target_precompile_headers(compas_pch INTERFACE src/compas.hpp)
target_include_directories(compas_pch INTERFACE
${EIGEN_INCLUDE_DIR}
${LIBIGL_INCLUDE_DIR}
${CLIPPER2_INCLUDE_DIR}
)
endif()

# Function to add a nanobind module with include directories
# === Function to add nanobind modules ===
function(add_nanobind_module module_name source_file)
nanobind_add_module(${module_name} STABLE_ABI NB_STATIC ${source_file})
add_dependencies(${module_name} clipper2_static)

# Ensure external dependencies are downloaded first
add_dependencies(${module_name} external_downloads)

# Add include directories and link PCH if enabled
if (ENABLE_PRECOMPILED_HEADERS)
target_link_libraries(${module_name} PRIVATE compas_pch)
else()
target_include_directories(${module_name} SYSTEM PRIVATE
${EIGEN_INCLUDE_DIR}
${LIBIGL_INCLUDE_DIR}
${CLIPPER2_INCLUDE_DIR}
)
endif()

target_link_libraries(${module_name} PRIVATE Threads::Threads)
target_link_libraries(${module_name} PRIVATE Threads::Threads clipper2_static)
install(TARGETS ${module_name} LIBRARY DESTINATION compas_libigl)
endfunction()

# Add modules
# === Add your modules ===
add_nanobind_module(_nanobind src/nanobind.cpp)
add_nanobind_module(_types_std src/types_std.cpp)
add_nanobind_module(_boundaries src/boundaries.cpp)
Expand Down
Loading
Loading