diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4241b3d..3afaf43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,15 +15,16 @@ jobs: fail-fast: false matrix: version: - - '1' - - '1.0' - - '1.3' + - '1' + - '1.6' os: - - [ubuntu-latest] + - ubuntu-latest + - macOS-latest + - windows-latest arch: - x64 steps: - # Cancel ongoing CI test runs if pushing to branch again before the previous tests + # Cancel ongoing CI test runs if pushing to branch again before the previous tests # have finished - name: Cancel ongoing test runs for previous commits uses: styfle/cancel-workflow-action@0.6.0 @@ -51,4 +52,4 @@ jobs: - uses: julia-actions/julia-processcoverage@v1 - uses: codecov/codecov-action@v1 with: - file: lcov.info \ No newline at end of file + file: lcov.info diff --git a/Project.toml b/Project.toml index cf709be..35dbc54 100644 --- a/Project.toml +++ b/Project.toml @@ -1,21 +1,18 @@ name = "MiniQhull" uuid = "978d7f02-9e05-4691-894f-ae31a51d76ca" authors = ["Francesc Verdugo ", "Victor Sande "] -version = "0.3.1" +version = "0.4.0" [deps] -CMake = "631607c0-34d2-5d66-819e-eb0f9aa2061a" -Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -Qhull_jll = "784f63db-0788-585a-bace-daefebcd302b" +QhullMiniWrapper_jll = "460c41e3-6112-5d7f-b78c-b6823adb3f2d" [compat] -CMake = "1.2.0" -Qhull_jll = "8" -julia = "1" +julia = "1.6" +QhullMiniWrapper_jll = "1" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "StaticArrays"] diff --git a/README.md b/README.md index f56fb8f..cce0202 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ delaunay(points::AbstractVector) -> Matrix{Int32} ``` In this variant, the cloud of points is specified with a vector of `dim`-element vectors or -tuples. It is also possible to use a vector of other tuple-like types, like `SVector` from +tuples. It is also possible to use a vector of other tuple-like types, like `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl). ### Adjusting Qhull flags @@ -80,67 +80,13 @@ connectivity = delaunay(pts, flags) ## Installation -`MiniQhull` is a registered Julia package. If your system fulfills all the requirements (see below), `MiniQhull` can be installed using the command: +`MiniQhull` is a registered Julia package. `MiniQhull` can be installed using the command: ``` pkg> add MiniQhull ``` -If, for any reason, you need to manually build the project, write down the following commands in the Julia REPL: -``` -pkg> add MiniQhull -pkg> build MiniQhull -``` - -### Requirements - -The `MiniQhull` package requires the [Qhull](http://www.qhull.org/) reentrant library installed in your system. - - - By default, if `julia >= 1.3`, it will use [`Qhull_jll` library](https://github.com/JuliaBinaryWrappers/Qhull_jll.jl) and you don't need to manually install anything. - - If you need to perform a custom `Qhull` installation. It can be installed in any path on your local machine as long as you export the environment variable `QHULL_ROOT_DIR` pointing to the installation directory. - - If none of the previous choices are used, `MiniQhull` will try to find the library in the usual linux user library directory (`/usr/lib`). - -`MiniQhull` also requires any C compiler installed on the system. - -#### Qhull installation - -##### From Sources - -Custom installation of `Qhull` can be performed as described in the official [Qhull installation instructions](http://www.qhull.org/README.txt). -You can find the latest source code in the oficial [Qhull download section](http://www.qhull.org/download/). - -##### Debian-based installation from package manager - -The reentrant `Qhull` library can be installed with `apt` in recent Debian-based linux distributions as follows: - -``` -$ sudo apt-get install update -$ sudo apt-get install libqhull-r7 libqhull-dev -``` - -If you need to install a C compiler, it can be also obtained by means of `apt` tool: -``` -$ sudo apt-get update -$ sudo apt-get install gcc build-essential -``` - -## Continuous integration - -In order to use `MiniQhull` in continuous integration jobs, you must ensure that its installation requirements are fullfilled in the CI environment. - -For `julia < 1.3` jobs, if your CI process is based on `Travis-CI` you can add the following block at the beginning of your `.travis.yml` file: - -``` -os: - - linux -dist: - - bionic -addons: - apt: - update: true - packages: - - gcc - - libqhull-r7 - - libqhull-dev -``` +`MiniQhull` depends two binary dependencies which are build for all Platforms by [BinaryBuilder](https://binarybuilder.org/): + - [Qhull_jll](https://github.com/JuliaBinaryWrappers/Qhull_jll.jl) + - [QhullMiniWrapper_jll](https://github.com/JuliaBinaryWrappers/QhullMiniWrapper_jll.jl) diff --git a/deps/MiniQhullWrapper/CMake/Modules/FindQhull.cmake b/deps/MiniQhullWrapper/CMake/Modules/FindQhull.cmake deleted file mode 100644 index 5e2252a..0000000 --- a/deps/MiniQhullWrapper/CMake/Modules/FindQhull.cmake +++ /dev/null @@ -1,58 +0,0 @@ -# - Find Qhull -# Find the Qhull library -# -# Specify components: -# -# :: -# qhull = Deprecated interface: use imported target Qhull::qhull -# qhullstatic = Recommended alternative to re-entrant interface: use imported target Qhull::qhullstatic -# qhull_r = Recommended (re-entrant) interface: use imported target Qhull::qhull_r -# qhullstatic_r = Recommended (re-entrant) interface: use imported target Qhull::qhullstatic_r -# qhullcpp = C++ interface: use imported target Qhull::libqhullcpp -# -# QHULL_FOUND - True if Qhull was found. -# -# Original Author: -# 2019 Ryan Pavlik -# -# Copyright Collabora, Ltd. 2019 -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) -# -# SPDX-License-Identifier: BSL-1.0 - -set(QHULL_ROOT_DIR - "${QHULL_ROOT_DIR}" - CACHE PATH "Directory to search for Qhull") - -find_path( - QHULL_qhull_INCLUDE_DIR - NAMES libqhull/libqhull.h - PATHS "${QHULL_ROOT_DIR}" "${QHULL_ROOT_DIR}/include") -find_path( - QHULL_qhull_r_INCLUDE_DIR - NAMES libqhull_r/libqhull_r.h - PATHS "${QHULL_ROOT_DIR}" "${QHULL_ROOT_DIR}/include") -find_path( - QHULL_qhullcpp_INCLUDE_DIR - NAMES libqhullcpp/Qhull.h - PATHS "${QHULL_ROOT_DIR}" "${QHULL_ROOT_DIR}/include") - -foreach(_qh_lib qhull qhull_r qhullstatic qhullstatic_r qhullcpp) - find_library( - QHULL_${_qh_lib}_LIBRARY - NAMES ${_qh_lib} lib${_qh_lib} - PATHS "${QHULL_ROOT_DIR}" "${QHULL_ROOT_DIR}/lib") -endforeach() - -mark_as_advanced( - QHULL_qhull_INCLUDE_DIR - QHULL_qhull_LIBRARY - QHULL_qhull_r_INCLUDE_DIR - QHULL_qhull_r_LIBRARY - QHULL_qhullcpp_INCLUDE_DIR - QHULL_qhullcpp_LIBRARY - QHULL_qhullstatic_LIBRARY - QHULL_qhullstatic_r_LIBRARY) - diff --git a/deps/MiniQhullWrapper/CMakeLists.txt b/deps/MiniQhullWrapper/CMakeLists.txt deleted file mode 100644 index 43fe2a2..0000000 --- a/deps/MiniQhullWrapper/CMakeLists.txt +++ /dev/null @@ -1,76 +0,0 @@ -################################################################# -# HEADER -################################################################# -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7) -PROJECT(MiniQhullWrapper C) - -SET(LIB ${PROJECT_NAME}) - -################################################################# -# DEFINE PATHS -################################################################# - -SET(CMAKE_PATH ${CMAKE_SOURCE_DIR}/CMake) -SET(SRC_PATH ${CMAKE_SOURCE_DIR}/src) -SET(CMAKE_MODULE_PATH "${CMAKE_PATH}/Modules") -SET(LIB_PATH ${SRC_PATH}/lib) -SET(INCLUDE_PATH ${SRC_PATH}/include) -SET(TESTS_PATH ${SRC_PATH}/tests) - -################################################################# -# BUILD PATHS -################################################################# - -SET(ROOT_OUTPUT_PATH ${PROJECT_BINARY_DIR}) -SET(LIBRARY_OUTPUT_PATH ${ROOT_OUTPUT_PATH}/lib) -SET(EXECUTABLE_OUTPUT_PATH ${ROOT_OUTPUT_PATH}/bin) - -################################################################# -# FIND QHULL -################################################################# - -FIND_PACKAGE(Qhull REQUIRED) -INCLUDE_DIRECTORIES(${INCLUDE_PATH} ${QHULL_qhull_r_INCLUDE_DIR}) - -################################################################# -# FLAGS depend on the compiler and the build type -################################################################# - -GET_FILENAME_COMPONENT(C_COMPILER_NAME ${CMAKE_C_COMPILER} NAME) - -message(STATUS "COMPILER INFO: ${CMAKE_C_COMPILER_ID} - ${C_COMPILER_NAME}") - -IF (${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR C_COMPILER_NAME MATCHES "gcc*") - # gcc -# set (CMAKE_C_FLAGS " " CACHE STRING "" FORCE) -ELSEIF (${CMAKE_C_COMPILER_ID} STREQUAL "Intel" OR C_COMPILER_NAME MATCHES "icc*") - # icc -# set (CMAKE_C_FLAGS " " CACHE STRING "" FORCE) -ENDIF () - -message (STATUS "CMAKE_C_COMPILER full path: " ${CMAKE_C_COMPILER}) -message (STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS}) -message (STATUS "CMAKE_C_FLAGS_RELEASE: " ${CMAKE_C_FLAGS_RELEASE}) -message (STATUS "CMAKE_C_FLAGS_DEBUG: " ${CMAKE_C_FLAGS_DEBUG}) - -################################################################# -# DYNAMIC LIBRARY -################################################################# -SET(BUILD_SHARED_LIBS True) - -################################################################# -# ADD SOURCE SUBDIRS -################################################################# - -ADD_SUBDIRECTORY(${LIB_PATH}) - -################################################################# -# ENABLE TESTING -################################################################# - -ENABLE_TESTING() -INCLUDE(CTest) -ADD_SUBDIRECTORY(${TESTS_PATH}) - - - diff --git a/deps/MiniQhullWrapper/src/include/qhullwrapper.h b/deps/MiniQhullWrapper/src/include/qhullwrapper.h deleted file mode 100644 index 6d23278..0000000 --- a/deps/MiniQhullWrapper/src/include/qhullwrapper.h +++ /dev/null @@ -1,8 +0,0 @@ - -#include - -qhT* new_qhull_handler(); -int delaunay_init_and_compute(qhT *qh, int dim, int numpoints, coordT *points, int* numcells, const char* options); -int delaunay_fill_cells(qhT *qh, int dim, int num_cells, int *cells); -int delaunay_free(qhT *qh); - diff --git a/deps/MiniQhullWrapper/src/lib/CMakeLists.txt b/deps/MiniQhullWrapper/src/lib/CMakeLists.txt deleted file mode 100644 index f6c62a2..0000000 --- a/deps/MiniQhullWrapper/src/lib/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -################################################################# -# Search C files recursively in all subdirs -################################################################# - -FILE(GLOB_RECURSE LIB_SRC *.c) -SET(LIB_SRC ${LIB_SRC} PARENT_SCOPE) - -################################################################# -# Library target -################################################################# -ADD_LIBRARY(${LIB} ${LIB_SRC}) - -TARGET_LINK_LIBRARIES(${LIB} ${QHULL_qhull_r_LIBRARY}) - -SET_TARGET_PROPERTIES(${LIB} PROPERTIES VERSION ${${LIB}_VERSION} SOVERSION ${${LIB}_SOVERSION}) -EXPORT(TARGETS ${LIB} APPEND FILE ${ROOT_OUTPUT_PATH}/${LIB}Targets.cmake) - diff --git a/deps/MiniQhullWrapper/src/lib/qhullwrapper.c b/deps/MiniQhullWrapper/src/lib/qhullwrapper.c deleted file mode 100644 index d74e28e..0000000 --- a/deps/MiniQhullWrapper/src/lib/qhullwrapper.c +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include - -qhT* new_qhull_handler() -{ - qhT qh_qh; - qhT *qh; - FILE *errfile= stderr; - - QHULL_LIB_CHECK - qh = (struct qhT*) malloc (sizeof (struct qhT)); - qh_zero(qh, errfile); - return qh; -} - -int delaunay_init_and_compute(qhT *qh, int dim, int numpoints, coordT *points, int* numcells, const char* flags) -{ - - boolT ismalloc = False; - FILE *outfile; - FILE *errfile= stderr; - char flagsarr[250]; - int exitcode; - int err; - facetT *facet; - - /* Set options */ - outfile = fopen("/dev/null","w"); - if (!outfile) {return -3;} - /*outfile= stdout;*/ - if (!flags) - { - if (dim <= 3) - flags = "qhull d Qt Qbb Qc Qz"; - else - flags = "qhull d Qt Qbb Qc Qx"; - } - snprintf(flagsarr, sizeof flagsarr, "%s", flags); - - /* Run qhull*/ - exitcode = qh_new_qhull(qh, dim, numpoints, points, ismalloc, flagsarr, outfile, errfile); - if (exitcode) return exitcode; - err = fclose(outfile); - if (err == EOF) {return -4;} - - /* Triangulate all the non-simplex cells generated by qhull*/ - qh_triangulate(qh); - - /* Count number of generated cells and double check that they are simplex*/ - (*numcells) = 0; - FORALLfacets - { - if (! facet->upperdelaunay) (*numcells)++; - if (! facet->simplicial) return -1; - } - - return 0; -} - -int delaunay_fill_cells(qhT *qh, int dim, int num_cells, int *cells) -{ - - int icell = 0; - facetT *facet; - vertexT *vertex, **vertexp; - int ivertex; - - /* Fill-in the cells connectivities */ - FORALLfacets - { - if (! facet->upperdelaunay) - { - ivertex = 0; - FOREACHvertex_ (facet->vertices) - { - cells[(dim+1)*icell+ivertex] = 1 + qh_pointid(qh, vertex->point); - ivertex++; - } - icell++; - } - } - - return 0; -} - -int delaunay_free(qhT *qh) -{ - - int curlong, totlong; - - /* Free memory from qhull and check that no memory is leaked */ - qh_freeqhull (qh, ! qh_ALL); - qh_memfreeshort (qh, &curlong, &totlong); - free(qh); - if (curlong || totlong) return -2; - - return 0; -} - diff --git a/deps/MiniQhullWrapper/src/tests/CMakeLists.txt b/deps/MiniQhullWrapper/src/tests/CMakeLists.txt deleted file mode 100644 index e2aaabf..0000000 --- a/deps/MiniQhullWrapper/src/tests/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -################################################################# -# TESTS -################################################################# - -FILE(GLOB_RECURSE TESTS_SRC *.c) -SET(TESTS_SRC ${TESTS_SRC} PARENT_SCOPE) - -################################################################# -# EXTERNAL LIBRARIES -################################################################# - -FOREACH(TEST_SRC ${TESTS_SRC}) - GET_FILENAME_COMPONENT(EXE_NAME ${TEST_SRC} NAME_WE) - ADD_EXECUTABLE(${EXE_NAME} ${TEST_SRC}) - TARGET_LINK_LIBRARIES(${EXE_NAME} ${LIB}) - ADD_TEST(${EXE_NAME}_TEST ${EXECUTABLE_OUTPUT_PATH}/${EXE_NAME}) -ENDFOREACH() - - - diff --git a/deps/MiniQhullWrapper/src/tests/testqhullwrapper.c b/deps/MiniQhullWrapper/src/tests/testqhullwrapper.c deleted file mode 100644 index eaddd7a..0000000 --- a/deps/MiniQhullWrapper/src/tests/testqhullwrapper.c +++ /dev/null @@ -1,35 +0,0 @@ - -#include -#include -#include - -int main(int argc, char *argv[]) { - - int error=0; - int numcells=0; - int dim = 2; - int numpoints = 4; - int *cells; - qhT *qh; - double points[] = {0.0,0.0,0.0,1.0,1.0,0.0,1.0,1.0}; - - qh = new_qhull_handler(); - printf("[ERROR=%d] (delaunay_init_and_compute) *qh = %p \n", error, qh); - error = delaunay_init_and_compute(qh, dim, numpoints, points, &numcells, NULL); - printf("[ERROR=%d] (delaunay_init_and_compute) numcells = %d \n", error, numcells); - if (error != 0) exit(error); - - cells = (int *) malloc((dim+1)*numcells * sizeof(int)); - error = delaunay_fill_cells(qh, dim, numcells, cells ); - printf("[ERROR=%d] (delaunay_fill_cells) cells [ ", error); - for(int i = 0; i < (dim+1)*numcells; i++) - printf("%d ", cells[i]); - printf("] \n"); - if (error != 0) exit(error); - - error = delaunay_free(qh); - if (error != 0) exit(error); - printf("[ERROR=%d] (delaunay_free) \n", error ); - - exit(error); -} diff --git a/deps/build.jl b/deps/build.jl deleted file mode 100644 index e411609..0000000 --- a/deps/build.jl +++ /dev/null @@ -1,111 +0,0 @@ -using CMake -using Libdl - -QHULL_WRAPPER_FOUND = true - -QHULL_WRAPPER_SOURCES = joinpath(@__DIR__, "MiniQhullWrapper") -QHULL_WRAPPER_BUILD = joinpath(QHULL_WRAPPER_SOURCES, "build") -QHULL_WRAPPER_LIB_DIR = joinpath(QHULL_WRAPPER_BUILD,"lib") - -QHULL_WRAPPER_LIB_NAME = "" -QHULL_WRAPPER_LIB_PATH = "" -QHULL_WRAPPER_LIB_NAMES = ["libMiniQhullWrapper.$(Libdl.dlext)"] - -QHULL_ROOT_DIR = "/usr" -if haskey(ENV,"QHULL_ROOT_DIR") - QHULL_ROOT_DIR = ENV["QHULL_ROOT_DIR"] -elseif VERSION >= v"1.3" - using Qhull_jll - QHULL_ROOT_DIR = Qhull_jll.artifact_dir -end - - -# Check QHULL_WRAPPER_SOURCES dir exists -if isdir(QHULL_WRAPPER_SOURCES) - @info "MiniQhullWrapper root directory at: $QHULL_WRAPPER_SOURCES" - - # Check QHULL_WRAPPER_BUILD exists - if isdir(QHULL_WRAPPER_BUILD) - rm(QHULL_WRAPPER_BUILD, recursive=true) - end - mkdir(QHULL_WRAPPER_BUILD) - - # Configure MiniQhullWrapper cmake project - try - configure = run(`$cmake -DQHULL_ROOT_DIR=$QHULL_ROOT_DIR -B $QHULL_WRAPPER_BUILD -S $QHULL_WRAPPER_SOURCES`) - if configure.exitcode != 0 - @warn "MiniQhullWrapper configure step fail with code: $(configure.exitcode)" - global QHULL_WRAPPER_FOUND=false - else - # Build MiniQhullWrapper cmake project - build = run(`$cmake --build $QHULL_WRAPPER_BUILD`) - if build.exitcode != 0 - @warn "MiniQhullWrapper build step fail with code: $(build.exitcode)" - global QHULL_WRAPPER_FOUND=false - else - # Test MiniQhullWrapper cmake project - test = run(`$cmake --build $QHULL_WRAPPER_BUILD --target test`) - if test.exitcode != 0 - @warn "MiniQhullWrapper test step fail with code: $(build.exitcode)" - global QHULL_WRAPPER_FOUND=false - end - end - end - catch e - @warn "Something wrong has happened while building MiniQhullWrapper" - global QHULL_WRAPPER_FOUND=false - end -else - @warn "MiniQhullWrapper root directory not found at: $QHULL_WRAPPER_SOURCES" - global QHULL_WRAPPER_FOUND=false -end - -if QHULL_WRAPPER_FOUND - # Check QHULL_LIB_DIR (.../lib directory) exists - if isdir(QHULL_WRAPPER_LIB_DIR) - @info "MiniQhullWrapper lib directory found at: $QHULL_WRAPPER_LIB_DIR" - else - QHULL_WRAPPER_LIB_DIR = "" - @warn "MiniQhullWrapper lib directory not found: $QHULL_WRAPPER_LIB_DIR" - end - - if isempty(QHULL_WRAPPER_LIB_NAME) - QHULL_WRAPPER_LIB_NAME=Libdl.find_library(QHULL_WRAPPER_LIB_NAMES, [QHULL_WRAPPER_LIB_DIR]) - end - - if isempty(QHULL_WRAPPER_LIB_NAME) - @warn "MiniQhullWrapper library not found: $QHULL_WRAPPER_LIB_NAMES" - QHULL_WRAPPER_FOUND = false - else - QHULL_WRAPPER_LIB_PATH=Libdl.dlpath(QHULL_WRAPPER_LIB_NAME) - @info "MiniQhullWrapper library found: $QHULL_WRAPPER_LIB_NAMES" - end -end - - -# Write QHULL configuration to deps.jl file -deps_jl = "deps.jl" - -if isfile(deps_jl) - rm(deps_jl) -end - -open(deps_jl,"w") do f - println(f, "# This file is automatically generated") - println(f, "# Do not edit") - println(f) - println(f, :(const QHULL_WRAPPER_FOUND = $QHULL_WRAPPER_FOUND)) - println(f, :(const QHULL_WRAPPER_LIB_DIR = $QHULL_WRAPPER_LIB_DIR)) - println(f, :(const QHULL_WRAPPER_LIB_NAME = $QHULL_WRAPPER_LIB_NAME)) - println(f, :(const QHULL_WRAPPER_LIB_PATH = $QHULL_WRAPPER_LIB_PATH)) -end - -@info """ -QHULL configuration: -============================================== - - QHULL_WRAPPER_FOUND = $QHULL_WRAPPER_FOUND - - QHULL_WRAPPER_LIB_DIR = $QHULL_WRAPPER_LIB_DIR - - QHULL_WRAPPER_LIB_NAME = $QHULL_WRAPPER_LIB_NAME - - QHULL_WRAPPER_LIB_PATH = $QHULL_WRAPPER_LIB_PATH -""" - diff --git a/src/MiniQhull.jl b/src/MiniQhull.jl index c59b34f..7d8eded 100644 --- a/src/MiniQhull.jl +++ b/src/MiniQhull.jl @@ -1,10 +1,9 @@ module MiniQhull -using Libdl +using QhullMiniWrapper_jll export delaunay -include("load.jl") include("bindings.jl") function _delaunay(dim::Int32, numpoints::Int32, points, flags::Union{Nothing,AbstractString}) @@ -13,7 +12,7 @@ function _delaunay(dim::Int32, numpoints::Int32, points, flags::Union{Nothing,Ab qh == C_NULL && error("Qhull handler is null") cflags = flags===nothing ? C_NULL : flags ierror = delaunay_init_and_compute(qh, dim, numpoints, points, numcells, cflags) - ierror != 0 && error("Failure on delaunay_init_and_compute function") + ierror != 0 && error("Failure on delaunay_init_and_compute function: $(ierror)") cells = Matrix{Int32}(undef,dim+1,numcells[]) ierror = delaunay_fill_cells(qh, dim, numcells[], cells) ierror != 0 && error("Failure on delaunay_fill_cells function") @@ -23,20 +22,20 @@ function _delaunay(dim::Int32, numpoints::Int32, points, flags::Union{Nothing,Ab end """ - delaunay(dim::Integer, numpoints::Integer, points::AbstractVector, + delaunay(dim::Integer, numpoints::Integer, points::AbstractVector, [flags::AbstractString]) → Matrix{Int32} Compute the Delaunay triangulation of a cloud of points in an arbitrary dimension `dim`. -The vector `points` holds the data in "component major order", i.e., components are -consecutive within the vector. The vector `points` therefore must have length +The vector `points` holds the data in "component major order", i.e., components are +consecutive within the vector. The vector `points` therefore must have length `dim * numpoints`. -The returned matrix has shape `(dim + 1, nsimplices)`, where `nsimplices` is the number of +The returned matrix has shape `(dim + 1, nsimplices)`, where `nsimplices` is the number of simplices in the computed Delaunay triangulation. You can override the default set of flags that Qhull uses by passing an additional -positional `flags` argument. The default set of flags is `qhull d Qt Qbb Qc Qz` for up to -3 dimensions, and `qhull d Qt Qbb Qc Qx` for higher dimensions. The flags you pass override +positional `flags` argument. The default set of flags is `qhull d Qt Qbb Qc Qz` for up to +3 dimensions, and `qhull d Qt Qbb Qc Qx` for higher dimensions. The flags you pass override the default flags, i.e. you have to pass all the flags that Qhull should use. ## Example: passing a column-major ordered vector of points @@ -58,7 +57,7 @@ connectivity = delaunay(dim, numpoints, coordinates) delaunay(points::AbstractMatrix, [flags::AbstractString]) -> Matrix{Int32} -In this variant, the cloud of points is specified by a matrix with +In this variant, the cloud of points is specified by a matrix with `size(matrix) == (dim, numpoints)`. ## Example: passing a matrix of points @@ -76,9 +75,9 @@ connectivity = delaunay(coordinates) delaunay(points::AbstractVector{T}, [flags::AbstractString]) where T -> Matrix{Int32} -In this variant, the cloud of points is specified by a vector of points. +In this variant, the cloud of points is specified by a vector of points. -`points` can a `Vector{Vector}` but this is slow, because the data needs to be flattened +`points` can a `Vector{Vector}` but this is slow, because the data needs to be flattened and collected before passing the data to Qhull. ```julia @@ -91,10 +90,10 @@ delaunay(pts) 1 1 ``` -A more efficient alternative is of `points` has the same memory layout as a column-major +A more efficient alternative is of `points` has the same memory layout as a column-major vector, that is, if `T` is some type such that `reinterpret(Float64, points)` yields -a column-major vector of the points. This avoids extra memory allocations, and is useful -if you have a large number of points to triangulate. Examples are using equal-length tuples +a column-major vector of the points. This avoids extra memory allocations, and is useful +if you have a large number of points to triangulate. Examples are using equal-length tuples or `SVector`s to represent the points. ```julia @@ -107,7 +106,7 @@ connectivity = delaunay(pts, flags) ``` """ -function delaunay end +function delaunay end function delaunay(dim::Integer, numpoints::Integer, points::AbstractVector{T}, flags::Union{Nothing,AbstractString}=nothing) where T @assert numpoints*dim == length(points) @@ -115,7 +114,7 @@ function delaunay(dim::Integer, numpoints::Integer, points::AbstractVector{T}, f end # If input data are already Float64, then no conversion is needed -function delaunay(dim::Integer, numpoints::Integer, points::AbstractVector{Float64}, flags::Union{Nothing,AbstractString}=nothing) +function delaunay(dim::Integer, numpoints::Integer, points::AbstractVector{Float64}, flags::Union{Nothing,AbstractString}=nothing) @assert numpoints*dim == length(points) _delaunay(Int32(dim), Int32(numpoints), points, flags) end @@ -131,20 +130,20 @@ get_dim(points::AbstractVector{T}) where T = length(eltype(points)) get_dim(points::AbstractVector{NTuple{N, T}}) where {N, T} = N get_dim(points::AbstractVector{T}) where T <: Vector = length(points[1]) -# Generic fallback for when points are of some type where it is possible to get a -# column-major vector by calling `reinterpret(Float64, points)`. This avoids extra -# allocations, and is more efficient than using regular vectors. +# Generic fallback for when points are of some type where it is possible to get a +# column-major vector by calling `reinterpret(Float64, points)`. This avoids extra +# allocations, and is more efficient than using regular vectors. # An example would be using `SVector`s or tuples as points. -function delaunay(points::AbstractVector{T}, flags=nothing) where {T} +function delaunay(points::AbstractVector{T}, flags=nothing) where {T} numpoints = length(points) _delaunay(Int32(get_dim(points)), Int32(numpoints), reinterpret(Float64, points), flags) end -# It is also conceivable that a user would want to use regular vectors as points. In that +# It is also conceivable that a user would want to use regular vectors as points. In that # case, we need to flatten the list of points and collect (allocates, and is slow). function delaunay(points::AbstractVector{T}, flags=nothing) where {T <: Vector} numpoints = length(points) - _delaunay(Int32(get_dim(points)), Int32(numpoints), + _delaunay(Int32(get_dim(points)), Int32(numpoints), Base.Iterators.Flatten(points) |> collect, flags) end diff --git a/src/bindings.jl b/src/bindings.jl index 51bb21e..f1edace 100644 --- a/src/bindings.jl +++ b/src/bindings.jl @@ -1,52 +1,45 @@ # qhT* new_qhull_handler(); function new_qhull_handler() - @check_if_loaded - ccall(new_qhull_handler_c[], Ptr{Cvoid}, (),) + ccall((:new_qhull_handler, qhullwrapper), Ptr{Cvoid}, (),) end # int delaunay_init_and_compute( int dim, int numpoints, coordT *points, int* numcells); function delaunay_init_and_compute(qh::Ptr{Cvoid}, dim::Int32, numpoints::Int32, points, numcells::Ref{Int32}, flags::Union{Ptr{Nothing},AbstractString}) - @check_if_loaded - ccall(delaunay_init_and_compute_c[], + ccall((:delaunay_init_and_compute, qhullwrapper), Cint, ( Ptr{Cvoid}, - Cint, + Cint, Cint, Ptr{Cdouble}, Ptr{Cint}, Cstring - ), + ), qh, - dim, - numpoints, - points, + dim, + numpoints, + points, numcells, flags - ) + ) end - # int delaunay_fill_cells(int dim, int num_cells, int *cells); function delaunay_fill_cells(qh::Ptr{Cvoid}, dim::Int32, numcells::Int32, cells::Array{Int32}) - @check_if_loaded - ccall(delaunay_fill_cells_c[], + ccall((:delaunay_fill_cells, qhullwrapper), Cint, ( Ptr{Cvoid}, - Cint, + Cint, Cint, Ptr{Cint} - ), + ), qh, - dim, + dim, numcells, cells) end #int delaunay_free() function delaunay_free(qh::Ptr{Cvoid}) - @check_if_loaded - ccall(delaunay_free_c[], Cint, (Ptr{Cvoid},),qh) + ccall((:delaunay_free, qhullwrapper), Cint, (Ptr{Cvoid},), qh) end - - diff --git a/src/load.jl b/src/load.jl deleted file mode 100644 index eecfb5a..0000000 --- a/src/load.jl +++ /dev/null @@ -1,39 +0,0 @@ -deps_jl = joinpath(@__DIR__, "..", "deps", "deps.jl") - -if !isfile(deps_jl) - error("Package MiniQhull not installed properly.") -end - -include(deps_jl) - -const new_qhull_handler_c = Ref{Ptr}() -const delaunay_init_and_compute_c = Ref{Ptr}() -const delaunay_fill_cells_c = Ref{Ptr}() -const delaunay_free_c = Ref{Ptr}() -const QHULL_WRAPPER_LOADED = Ref(false) - -function __init__() - if QHULL_WRAPPER_FOUND - flags = Libdl.RTLD_LAZY | Libdl.RTLD_DEEPBIND | Libdl.RTLD_GLOBAL - QHULL_WRAPPER = Libdl.dlopen(QHULL_WRAPPER_LIB_PATH, flags) - - MiniQhull.new_qhull_handler_c[] = Libdl.dlsym(QHULL_WRAPPER,:new_qhull_handler) - MiniQhull.delaunay_init_and_compute_c[] = Libdl.dlsym(QHULL_WRAPPER,:delaunay_init_and_compute) - MiniQhull.delaunay_fill_cells_c[] = Libdl.dlsym(QHULL_WRAPPER,:delaunay_fill_cells) - MiniQhull.delaunay_free_c[] = Libdl.dlsym(QHULL_WRAPPER,:delaunay_free) - QHULL_WRAPPER_LOADED[] = true - else - QHULL_WRAPPER_LOADED[] = false - end -end - - -macro check_if_loaded() - quote - if ! QHULL_WRAPPER_LOADED[] - error("QHULL WRAPPER is not properly loaded") - end - end -end - - diff --git a/test/runtests.jl b/test/runtests.jl index 82a3e23..4db66c5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,46 +2,43 @@ using MiniQhull using Test using StaticArrays -if MiniQhull.QHULL_WRAPPER_LOADED[] - @testset "MiniQhull" begin - @test delaunay(2,4,[0,0,0,1,1,0,1,1]) == delaunay([0 0 1 1; 0 1 0 1]) - end +@testset "MiniQhull" begin + @test delaunay(2,4,[0,0,0,1,1,0,1,1]) == delaunay([0 0 1 1; 0 1 0 1]) +end - @testset "Custom array-type" begin - pts = [[0.,0.], [0.,1.], [1.,0.], [1.,1.]] - dim = 2 - x = reinterpret(Float64, [SVector{dim, Float64}(pt) for pt in pts]) - @test delaunay(dim, length(pts), x) == [4 4; 2 3; 1 1] - end +@testset "Custom array-type" begin + pts = [[0.,0.], [0.,1.], [1.,0.], [1.,1.]] + dim = 2 + x = reinterpret(Float64, [SVector{dim, Float64}(pt) for pt in pts]) + @test delaunay(dim, length(pts), x) == [4 4; 2 3; 1 1] +end - @testset "Vector of vectors" begin - pts = [[0.,0.], [0.,1.], [1.,0.], [1.,1.]] +@testset "Vector of vectors" begin + pts = [[0.,0.], [0.,1.], [1.,0.], [1.,1.]] - @testset "Vector of regular vectors" begin - @test delaunay(pts) == [4 4; 2 3; 1 1] - end + @testset "Vector of regular vectors" begin + @test delaunay(pts) == [4 4; 2 3; 1 1] + end - @testset "Vector of tuples" begin - tups = [(0.,0.,), (0.,1.,), (1.,0.,), (1.,1., )] - @test delaunay(tups) == [4 4; 2 3; 1 1] - end - - @testset "Vector of SVectors" begin - @test delaunay([SVector{2, Float64}(pt) for pt in pts]) == [4 4; 2 3; 1 1] - end + @testset "Vector of tuples" begin + tups = [(0.,0.,), (0.,1.,), (1.,0.,), (1.,1., )] + @test delaunay(tups) == [4 4; 2 3; 1 1] end - @testset "MiniQhull with flags" begin - c0 = delaunay([0 0 1 1; 0 1 0 1]) - c1 = delaunay([0 0 1 1; 0 1 0 1], nothing) - @test c1 == c0 - c2 = delaunay([0 0 1 1; 0 1 0 1], "qhull d Qt Qbb Qc Qz") - @test c2 == c0 - c3 = delaunay([0 0 1 1; 0 1 0 1], "qhull d Qbb Qc QJ Pp") - # QJ randomizes ("joggles") the input, so the output is not deterministic - @test size(c3) == (3, 2) - c4 = delaunay([0 0 1 1; 0 1 0 1], GenericString("qhull d Qt Qbb Qc Qz")) - @test c4 == c0 + @testset "Vector of SVectors" begin + @test delaunay([SVector{2, Float64}(pt) for pt in pts]) == [4 4; 2 3; 1 1] end +end +@testset "MiniQhull with flags" begin + c0 = delaunay([0 0 1 1; 0 1 0 1]) + c1 = delaunay([0 0 1 1; 0 1 0 1], nothing) + @test c1 == c0 + c2 = delaunay([0 0 1 1; 0 1 0 1], "qhull d Qt Qbb Qc Qz") + @test c2 == c0 + c3 = delaunay([0 0 1 1; 0 1 0 1], "qhull d Qbb Qc QJ Pp") + # QJ randomizes ("joggles") the input, so the output is not deterministic + @test size(c3) == (3, 2) + c4 = delaunay([0 0 1 1; 0 1 0 1], GenericString("qhull d Qt Qbb Qc Qz")) + @test c4 == c0 end