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
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
groups:
actions:
patterns: ["*"]
15 changes: 15 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name-template: $RESOLVED_VERSION
tag-template: $RESOLVED_VERSION
# The pull request labels choose the next version; unlabeled ones count as patch
categories:
- type: version-resolver
semver-increment: major
when:
label: major
- type: version-resolver
semver-increment: minor
when:
label: minor
- type: version-resolver
template: |
$CHANGES
60 changes: 27 additions & 33 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,47 @@ on:
branches: [master]
pull_request:

# Cancel superseded runs on the same branch/PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-test:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: pipx run pre-commit run --all-files --show-diff-on-failure

test:
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.13", "3.14t"]

defaults:
run:
shell: bash

python-version: ["3.10", "3.14", "3.14t"]
steps:
- name: Check out with submodules
uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
submodules: recursive

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
submodules: true
fetch-depth: 0
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install --upgrade pip
- run: python -m pip install . --group test
- run: python -m pytest ${{ endsWith(matrix.python-version, 't') && '--parallel-threads=4' || '' }}

- name: Install build and test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build
python -m pip install -r requirements.txt

- name: Build the wheel
run: python -m build --wheel --outdir dist

- name: Install the built wheel
run: python -m pip install --no-index --find-links dist PythonCDT

- name: Run tests
run: pytest ./cdt_bindings_test.py

- name: Upload wheel
uses: actions/upload-artifact@v4
stubs:
name: Type stubs match the module
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
submodules: true
fetch-depth: 0
- uses: actions/setup-python@v7
with:
name: wheel-${{ matrix.os }}-py${{ matrix.python-version }}
path: dist/*.whl
python-version: "3.x"
- run: python -m pip install . mypy
- run: python -m mypy.stubtest pythoncdt --allowlist tests/stubtest-allowlist.txt
15 changes: 15 additions & 0 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Release Drafter

on:
push:
branches: [master]

permissions:
contents: write
pull-requests: read

jobs:
draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v7
63 changes: 63 additions & 0 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Wheels

on:
pull_request:
release:
types: [published]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
sdist:
name: Source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
submodules: true
fetch-depth: 0 # the version comes from git tags
# Builds the sdist, then a wheel from it: proves the sdist is complete
- run: pipx run build
- uses: actions/upload-artifact@v7
with:
name: dist-sdist
path: dist/*.tar.gz

wheels:
name: Wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest]
steps:
- uses: actions/checkout@v7
with:
submodules: true
fetch-depth: 0
- uses: pypa/cibuildwheel@v4.3.0
- uses: actions/upload-artifact@v7
with:
name: dist-wheels-${{ matrix.os }}
path: wheelhouse/*.whl

publish:
name: Publish to PyPI
if: github.event_name == 'release' && github.repository == 'artem-ogre/PythonCDT'
needs: [sdist, wheels]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/pythoncdt
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v8
with:
pattern: dist-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ cmake-build-*/*
python/.venv/*
.venv/*
out/
wheelhouse/
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "CDT"]
path = CDT
url = https://github.com/artem-ogre/CDT.git
[submodule "pybind11"]
path = pybind11
url = https://github.com/pybind/pybind11.git
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.10
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format
36 changes: 10 additions & 26 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
cmake_minimum_required(VERSION 3.13.0)
cmake_minimum_required(VERSION 3.15...4.0)
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)

set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
find_package(pybind11 CONFIG REQUIRED)
add_subdirectory(CDT/CDT)

project(
PythonCDT
VERSION 2.0.0
DESCRIPTION "Python bindings for CDT: constrained Delaunay triangulation"
LANGUAGES CXX
)
pybind11_add_module(_core src/cdt_bindings.cpp)
target_compile_features(_core PRIVATE cxx_std_17)
target_compile_definitions(_core PRIVATE VERSION_INFO="${SKBUILD_PROJECT_VERSION_FULL}")
target_link_libraries(_core PRIVATE CDT::CDT)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

add_subdirectory(CDT/CDT CDT)
add_subdirectory(pybind11)
pybind11_add_module(PythonCDT cdt_bindings.cpp)
target_include_directories(
PythonCDT PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
)
target_link_libraries(PythonCDT PRIVATE CDT::CDT)
# Exposed to Python as PythonCDT.__version__. setup.py passes the wheel
# version; fall back to the project version for plain CMake builds.
if(NOT DEFINED VERSION_INFO)
set(VERSION_INFO ${PROJECT_VERSION})
endif()
target_compile_definitions(PythonCDT PRIVATE VERSION_INFO=${VERSION_INFO})
# Use rasterizer as pre-compiled header for faster test-only re-compiles
#target_precompile_headers(PythonCDT PRIVATE include/rasterizer.h)
# Installed as a component, so that CDT's own install rules stay out of the wheel
install(TARGETS _core DESTINATION pythoncdt COMPONENT python)
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,35 @@
Python bindings for [CDT: C++ library for constrained Delaunay triangulation](https://github.com/artem-ogre/CDT) implemented with [pybind11](https://github.com/pybind/pybind11)

***If PythonCDT helped you please consider adding a star on [GitHub](https://github.com/artem-ogre/PythonCDT). This means a lot to the authors*** 🤩
## Building

### Pre-conditions
- Clone with submodules: `git clone --recurse-submodules https://github.com/artem-ogre/PythonCDT.git`
- Make sure packages from requirements.txt are available.
## Installation

```bash
# build the wheel and install the package with pip
pip3 install .
# run tests
pytest ./cdt_bindings_test.py
pip install pythoncdt
```

Wheels are published for Linux, macOS and Windows.

## Building from source

Building needs a C++17 compiler; pip fetches pybind11, and CMake if it is missing.

```bash
git clone --recurse-submodules https://github.com/artem-ogre/PythonCDT.git
cd PythonCDT
pip install . --group test # needs pip 25.1+
pytest
```

Before committing, install the [pre-commit](https://pre-commit.com) hooks with `pre-commit install`: CI runs the same checks.

## Usage examples

### Constrained triangulation

```python
import numpy as np
import PythonCDT as cdt
import pythoncdt as cdt

vertices = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.4, 0.4]])
edges = np.array([[0, 1], [1, 2], [2, 3], [3, 0]], dtype=np.uintc)
Expand All @@ -32,9 +41,9 @@ t.insert_vertices(vertices)
t.insert_edges(edges)
t.erase_outer_triangles_and_holes()

vv = t.vertices_array() # numpy array with fields 'x' and 'y'
vv = t.vertices_array() # numpy array with fields 'x' and 'y'
tt = t.triangles_array() # numpy array with fields 'vertices' and 'neighbors'
tt["vertices"] # (T, 3) vertex indices into vv
tt["vertices"] # (T, 3) vertex indices into vv
```

#### Notes
Expand Down Expand Up @@ -88,3 +97,5 @@ Iterators (`*_iter()`) and `copy=False` views are not protected: don't use them
## Contributors
- [SioulisChris](https://github.com/SioulisChris): fixing the tests on Windows
- [sccolbert](https://github.com/sccolbert): reading the triangulation back as numpy arrays, releasing the GIL
- [mdealencar](https://github.com/mdealencar): packaging for PyPI
- [aidannewsome](https://github.com/aidannewsome): free-threading support, building wheels for PyPI
2 changes: 1 addition & 1 deletion benchmark_gil_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import numpy as np

import PythonCDT as cdt
import pythoncdt as cdt

N_THREADS = min(8, os.cpu_count())
N_VERTICES = 200_000
Expand Down
5 changes: 3 additions & 2 deletions benchmark_list_vs_array_access.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"""Compares reading a triangulation as Python lists vs numpy arrays"""

import timeit
from functools import partial

import numpy as np

import PythonCDT as cdt
import pythoncdt as cdt

t = cdt.Triangulation(cdt.VertexInsertionOrder.AUTO, cdt.IntersectingConstraintEdges.NOT_ALLOWED, 0.0)
t.insert_vertices(np.random.default_rng(0).random((200_000, 2)))

print(f"{'':<10}{'count':>9}{'list':>12}{'copy':>12}{'view':>12}")
for name in ["vertices", "triangles"]:
array = getattr(t, f"{name}_array")
ways = [lambda: getattr(t, name), array, lambda: array(copy=False)]
ways = [partial(getattr, t, name), array, partial(array, copy=False)]
ms = [1e3 * min(timeit.repeat(way, number=1, repeat=5)) for way in ways]
print(f"{name:<10}{len(array()):>9}" + "".join(f"{m:>9.3f} ms" for m in ms))
1 change: 0 additions & 1 deletion pybind11
Submodule pybind11 deleted from d03662
Loading
Loading