├── src └── mesher │ ├── __init__.py │ ├── bindings.cpp │ └── cgal_mesher.cpp ├── examples ├── triangulate2.py ├── triangulate3.py ├── triangulate4.py ├── triangulate5.py ├── make_mesh.py ├── triangulate6.py ├── triangulate7.py ├── triangulate8.py ├── triangulate9.py ├── triangulate10.py ├── example_final.py └── triangulate11.py ├── README.md ├── CMakeLists.txt ├── setup.py └── .gitignore /src/mesher/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/triangulate2.py: -------------------------------------------------------------------------------- 1 | import mesher.cgal_mesher 2 | -------------------------------------------------------------------------------- /examples/triangulate3.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | 3 | 4 | def main(): 5 | cdt = CDT() 6 | 7 | 8 | if __name__ == '__main__': 9 | main() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mesher 2 | Example of wrapping CGAL Delaunay triangulations and mesh refinement using pybind11 3 | 4 | This code was first created and used for a talk "Integrate Python and C++ with pybind11" 5 | at NDC Techtown 2018 in Kongsberg, Norway. 6 | 7 | You can find a video of the talk at https://www.youtube.com/watch?v=YReJ3pSnNDo 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11.0) 2 | 3 | set(CMAKE_VERBOSE_MAKEFILE ON) 4 | 5 | project(cgal_mesher) 6 | 7 | # CGAL and its components 8 | find_package( CGAL QUIET COMPONENTS Core GMP MPFI ) 9 | 10 | if ( NOT CGAL_FOUND ) 11 | 12 | message(STATUS "This project requires the CGAL library, and will not be compiled.") 13 | return() 14 | 15 | endif() 16 | 17 | # include helper file 18 | include( ${CGAL_USE_FILE} ) 19 | 20 | 21 | # Boost and its components 22 | find_package( Boost REQUIRED ) 23 | 24 | if ( NOT Boost_FOUND ) 25 | 26 | message(STATUS "This project requires the Boost library, and will not be compiled.") 27 | 28 | return() 29 | 30 | endif() 31 | 32 | set(SOURCE_DIR "src/mesher") 33 | include_directories(${SOURCE_DIR}) 34 | 35 | 36 | add_subdirectory(lib/pybind11) 37 | pybind11_add_module(cgal_mesher ${SOURCES} "${SOURCE_DIR}/cgal_mesher.cpp") 38 | 39 | -------------------------------------------------------------------------------- /examples/triangulate4.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import Point 3 | 4 | def main(): 5 | cdt = CDT() 6 | 7 | va = cdt.insert(Point(100, 269)) 8 | vb = cdt.insert(Point(246, 269)) 9 | vc = cdt.insert(Point(246, 223)) 10 | vd = cdt.insert(Point(303, 223)) 11 | ve = cdt.insert(Point(303, 298)) 12 | vf = cdt.insert(Point(246, 298)) 13 | vg = cdt.insert(Point(246, 338)) 14 | vh = cdt.insert(Point(355, 338)) 15 | vi = cdt.insert(Point(355, 519)) 16 | vj = cdt.insert(Point(551, 519)) 17 | vk = cdt.insert(Point(551, 445)) 18 | vl = cdt.insert(Point(463, 445)) 19 | vm = cdt.insert(Point(463, 377)) 20 | vn = cdt.insert(Point(708, 377)) 21 | vo = cdt.insert(Point(708, 229)) 22 | vp = cdt.insert(Point(435, 229)) 23 | vq = cdt.insert(Point(435, 100)) 24 | vr = cdt.insert(Point(100, 100)) 25 | 26 | if __name__ == '__main__': 27 | main() -------------------------------------------------------------------------------- /examples/triangulate5.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import Point 3 | 4 | def main(): 5 | cdt = CDT() 6 | 7 | va = cdt.insert(Point(100, 269)) 8 | vb = cdt.insert(Point(246, 269)) 9 | vc = cdt.insert(Point(246, 223)) 10 | vd = cdt.insert(Point(303, 223)) 11 | ve = cdt.insert(Point(303, 298)) 12 | vf = cdt.insert(Point(246, 298)) 13 | vg = cdt.insert(Point(246, 338)) 14 | vh = cdt.insert(Point(355, 338)) 15 | vi = cdt.insert(Point(355, 519)) 16 | vj = cdt.insert(Point(551, 519)) 17 | vk = cdt.insert(Point(551, 445)) 18 | vl = cdt.insert(Point(463, 445)) 19 | vm = cdt.insert(Point(463, 377)) 20 | vn = cdt.insert(Point(708, 377)) 21 | vo = cdt.insert(Point(708, 229)) 22 | vp = cdt.insert(Point(435, 229)) 23 | vq = cdt.insert(Point(435, 100)) 24 | vr = cdt.insert(Point(100, 100)) 25 | 26 | cdt.insert_constraint(va, vb) 27 | cdt.insert_constraint(vb, vc) 28 | cdt.insert_constraint(vc, vd) 29 | cdt.insert_constraint(vd, ve) 30 | cdt.insert_constraint(ve, vf) 31 | cdt.insert_constraint(vf, vg) 32 | cdt.insert_constraint(vg, vh) 33 | cdt.insert_constraint(vh, vi) 34 | cdt.insert_constraint(vi, vj) 35 | cdt.insert_constraint(vj, vk) 36 | cdt.insert_constraint(vk, vl) 37 | cdt.insert_constraint(vl, vm) 38 | cdt.insert_constraint(vm, vn) 39 | cdt.insert_constraint(vn, vo) 40 | cdt.insert_constraint(vo, vp) 41 | cdt.insert_constraint(vp, vq) 42 | cdt.insert_constraint(vq, vr) 43 | cdt.insert_constraint(vr, va) 44 | 45 | if __name__ == '__main__': 46 | main() -------------------------------------------------------------------------------- /examples/make_mesh.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 4 | from mesher.cgal_mesher import ( 5 | Point, 6 | Mesher, 7 | make_conforming_delaunay, 8 | make_conforming_gabriel, 9 | Criteria, 10 | lloyd_optimize 11 | ) 12 | 13 | 14 | def main(argv=None): 15 | if argv is None: 16 | argv = sys.argv[1:] 17 | 18 | edge_filename = argv[0] 19 | mesh_filename = argv[1] 20 | mesh_size = float(argv[2]) 21 | 22 | cdt = CDT() 23 | with open(edge_filename, 'rt') as edge_file: 24 | for line in edge_file: 25 | ax, ay, bx, by = map(float, line.split()) 26 | a = cdt.insert(Point(ax, ay)) 27 | b = cdt.insert(Point(bx, by)) 28 | cdt.insert_constraint(a, b) 29 | 30 | make_conforming_delaunay(cdt) 31 | make_conforming_gabriel(cdt) 32 | 33 | mesher = Mesher(cdt) 34 | mesher.criteria = Criteria( 35 | aspect_bound=0.125, 36 | size_bound=mesh_size 37 | ) 38 | mesher.refine_mesh() 39 | 40 | lloyd_optimize(cdt, time_limit=0.5) 41 | 42 | point_to_index_map = { 43 | vertex.point: index 44 | for index, vertex in enumerate(cdt.finite_vertices()) 45 | } 46 | 47 | triangles = ( 48 | tuple( 49 | point_to_index_map[face.vertex_handle(i).point] 50 | for i in range(3) 51 | ) 52 | for face in cdt.finite_faces() 53 | ) 54 | 55 | with open(mesh_filename, 'wt') as mesh_file: 56 | print(cdt.number_of_vertices(), file=mesh_file) 57 | for point in point_to_index_map: 58 | print(point.x, point.y, file=mesh_file) 59 | 60 | print(cdt.number_of_faces(), file=mesh_file) 61 | for triangle in triangles: 62 | print(' '.join(map(str, triangle)), file=mesh_file) 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /examples/triangulate6.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import Point 3 | 4 | def main(): 5 | cdt = CDT() 6 | 7 | va = cdt.insert(Point(100, 269)) 8 | vb = cdt.insert(Point(246, 269)) 9 | vc = cdt.insert(Point(246, 223)) 10 | vd = cdt.insert(Point(303, 223)) 11 | ve = cdt.insert(Point(303, 298)) 12 | vf = cdt.insert(Point(246, 298)) 13 | vg = cdt.insert(Point(246, 338)) 14 | vh = cdt.insert(Point(355, 338)) 15 | vi = cdt.insert(Point(355, 519)) 16 | vj = cdt.insert(Point(551, 519)) 17 | vk = cdt.insert(Point(551, 445)) 18 | vl = cdt.insert(Point(463, 445)) 19 | vm = cdt.insert(Point(463, 377)) 20 | vn = cdt.insert(Point(708, 377)) 21 | vo = cdt.insert(Point(708, 229)) 22 | vp = cdt.insert(Point(435, 229)) 23 | vq = cdt.insert(Point(435, 100)) 24 | vr = cdt.insert(Point(100, 100)) 25 | 26 | cdt.insert_constraint(va, vb) 27 | cdt.insert_constraint(vb, vc) 28 | cdt.insert_constraint(vc, vd) 29 | cdt.insert_constraint(vd, ve) 30 | cdt.insert_constraint(ve, vf) 31 | cdt.insert_constraint(vf, vg) 32 | cdt.insert_constraint(vg, vh) 33 | cdt.insert_constraint(vh, vi) 34 | cdt.insert_constraint(vi, vj) 35 | cdt.insert_constraint(vj, vk) 36 | cdt.insert_constraint(vk, vl) 37 | cdt.insert_constraint(vl, vm) 38 | cdt.insert_constraint(vm, vn) 39 | cdt.insert_constraint(vn, vo) 40 | cdt.insert_constraint(vo, vp) 41 | cdt.insert_constraint(vp, vq) 42 | cdt.insert_constraint(vq, vr) 43 | cdt.insert_constraint(vr, va) 44 | 45 | vs = cdt.insert(Point(349, 236)) 46 | vt = cdt.insert(Point(370, 236)) 47 | vu = cdt.insert(Point(370, 192)) 48 | vv = cdt.insert(Point(403, 192)) 49 | vw = cdt.insert(Point(403, 158)) 50 | vx = cdt.insert(Point(349, 158)) 51 | 52 | cdt.insert_constraint(vs, vt) 53 | cdt.insert_constraint(vt, vu) 54 | cdt.insert_constraint(vu, vv) 55 | cdt.insert_constraint(vv, vw) 56 | cdt.insert_constraint(vw, vx) 57 | cdt.insert_constraint(vx, vs) 58 | 59 | vy = cdt.insert(Point(501, 336)) 60 | vz = cdt.insert(Point(533, 336)) 61 | v1 = cdt.insert(Point(519, 307)) 62 | v2 = cdt.insert(Point(484, 307)) 63 | 64 | cdt.insert_constraint(vy, vz) 65 | cdt.insert_constraint(vz, v1) 66 | cdt.insert_constraint(v1, v2) 67 | cdt.insert_constraint(v2, vy) 68 | 69 | print("Number of vertices:", cdt.number_of_vertices()) 70 | 71 | if __name__ == '__main__': 72 | main() -------------------------------------------------------------------------------- /examples/triangulate7.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import ( 3 | Point, Mesher 4 | ) 5 | 6 | def main(): 7 | cdt = CDT() 8 | 9 | va = cdt.insert(Point(100, 269)) 10 | vb = cdt.insert(Point(246, 269)) 11 | vc = cdt.insert(Point(246, 223)) 12 | vd = cdt.insert(Point(303, 223)) 13 | ve = cdt.insert(Point(303, 298)) 14 | vf = cdt.insert(Point(246, 298)) 15 | vg = cdt.insert(Point(246, 338)) 16 | vh = cdt.insert(Point(355, 338)) 17 | vi = cdt.insert(Point(355, 519)) 18 | vj = cdt.insert(Point(551, 519)) 19 | vk = cdt.insert(Point(551, 445)) 20 | vl = cdt.insert(Point(463, 445)) 21 | vm = cdt.insert(Point(463, 377)) 22 | vn = cdt.insert(Point(708, 377)) 23 | vo = cdt.insert(Point(708, 229)) 24 | vp = cdt.insert(Point(435, 229)) 25 | vq = cdt.insert(Point(435, 100)) 26 | vr = cdt.insert(Point(100, 100)) 27 | 28 | cdt.insert_constraint(va, vb) 29 | cdt.insert_constraint(vb, vc) 30 | cdt.insert_constraint(vc, vd) 31 | cdt.insert_constraint(vd, ve) 32 | cdt.insert_constraint(ve, vf) 33 | cdt.insert_constraint(vf, vg) 34 | cdt.insert_constraint(vg, vh) 35 | cdt.insert_constraint(vh, vi) 36 | cdt.insert_constraint(vi, vj) 37 | cdt.insert_constraint(vj, vk) 38 | cdt.insert_constraint(vk, vl) 39 | cdt.insert_constraint(vl, vm) 40 | cdt.insert_constraint(vm, vn) 41 | cdt.insert_constraint(vn, vo) 42 | cdt.insert_constraint(vo, vp) 43 | cdt.insert_constraint(vp, vq) 44 | cdt.insert_constraint(vq, vr) 45 | cdt.insert_constraint(vr, va) 46 | 47 | vs = cdt.insert(Point(349, 236)) 48 | vt = cdt.insert(Point(370, 236)) 49 | vu = cdt.insert(Point(370, 192)) 50 | vv = cdt.insert(Point(403, 192)) 51 | vw = cdt.insert(Point(403, 158)) 52 | vx = cdt.insert(Point(349, 158)) 53 | 54 | cdt.insert_constraint(vs, vt) 55 | cdt.insert_constraint(vt, vu) 56 | cdt.insert_constraint(vu, vv) 57 | cdt.insert_constraint(vv, vw) 58 | cdt.insert_constraint(vw, vx) 59 | cdt.insert_constraint(vx, vs) 60 | 61 | vy = cdt.insert(Point(501, 336)) 62 | vz = cdt.insert(Point(533, 336)) 63 | v1 = cdt.insert(Point(519, 307)) 64 | v2 = cdt.insert(Point(484, 307)) 65 | 66 | cdt.insert_constraint(vy, vz) 67 | cdt.insert_constraint(vz, v1) 68 | cdt.insert_constraint(v1, v2) 69 | cdt.insert_constraint(v2, vy) 70 | 71 | print("Number of vertices:", cdt.number_of_vertices()) 72 | 73 | mesher = Mesher(cdt) 74 | seeds = [ 75 | Point(505, 325), 76 | Point(379, 172), 77 | ] 78 | mesher.seeds_from(seeds) 79 | 80 | if __name__ == '__main__': 81 | main() 82 | -------------------------------------------------------------------------------- /examples/triangulate8.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import ( 3 | Point, Mesher, make_conforming_delaunay, make_conforming_gabriel 4 | ) 5 | 6 | def main(): 7 | cdt = CDT() 8 | 9 | va = cdt.insert(Point(100, 269)) 10 | vb = cdt.insert(Point(246, 269)) 11 | vc = cdt.insert(Point(246, 223)) 12 | vd = cdt.insert(Point(303, 223)) 13 | ve = cdt.insert(Point(303, 298)) 14 | vf = cdt.insert(Point(246, 298)) 15 | vg = cdt.insert(Point(246, 338)) 16 | vh = cdt.insert(Point(355, 338)) 17 | vi = cdt.insert(Point(355, 519)) 18 | vj = cdt.insert(Point(551, 519)) 19 | vk = cdt.insert(Point(551, 445)) 20 | vl = cdt.insert(Point(463, 445)) 21 | vm = cdt.insert(Point(463, 377)) 22 | vn = cdt.insert(Point(708, 377)) 23 | vo = cdt.insert(Point(708, 229)) 24 | vp = cdt.insert(Point(435, 229)) 25 | vq = cdt.insert(Point(435, 100)) 26 | vr = cdt.insert(Point(100, 100)) 27 | 28 | cdt.insert_constraint(va, vb) 29 | cdt.insert_constraint(vb, vc) 30 | cdt.insert_constraint(vc, vd) 31 | cdt.insert_constraint(vd, ve) 32 | cdt.insert_constraint(ve, vf) 33 | cdt.insert_constraint(vf, vg) 34 | cdt.insert_constraint(vg, vh) 35 | cdt.insert_constraint(vh, vi) 36 | cdt.insert_constraint(vi, vj) 37 | cdt.insert_constraint(vj, vk) 38 | cdt.insert_constraint(vk, vl) 39 | cdt.insert_constraint(vl, vm) 40 | cdt.insert_constraint(vm, vn) 41 | cdt.insert_constraint(vn, vo) 42 | cdt.insert_constraint(vo, vp) 43 | cdt.insert_constraint(vp, vq) 44 | cdt.insert_constraint(vq, vr) 45 | cdt.insert_constraint(vr, va) 46 | 47 | vs = cdt.insert(Point(349, 236)) 48 | vt = cdt.insert(Point(370, 236)) 49 | vu = cdt.insert(Point(370, 192)) 50 | vv = cdt.insert(Point(403, 192)) 51 | vw = cdt.insert(Point(403, 158)) 52 | vx = cdt.insert(Point(349, 158)) 53 | 54 | cdt.insert_constraint(vs, vt) 55 | cdt.insert_constraint(vt, vu) 56 | cdt.insert_constraint(vu, vv) 57 | cdt.insert_constraint(vv, vw) 58 | cdt.insert_constraint(vw, vx) 59 | cdt.insert_constraint(vx, vs) 60 | 61 | vy = cdt.insert(Point(501, 336)) 62 | vz = cdt.insert(Point(533, 336)) 63 | v1 = cdt.insert(Point(519, 307)) 64 | v2 = cdt.insert(Point(484, 307)) 65 | 66 | cdt.insert_constraint(vy, vz) 67 | cdt.insert_constraint(vz, v1) 68 | cdt.insert_constraint(v1, v2) 69 | cdt.insert_constraint(v2, vy) 70 | 71 | print("Number of vertices:", cdt.number_of_vertices()) 72 | 73 | mesher = Mesher(cdt) 74 | seeds = [ 75 | Point(505, 325), 76 | Point(379, 172), 77 | ] 78 | mesher.seeds_from(seeds) 79 | 80 | make_conforming_delaunay(cdt) 81 | print("Number of vertices:", cdt.number_of_vertices()) 82 | 83 | make_conforming_gabriel(cdt) 84 | print("Number of vertices:", cdt.number_of_vertices()) 85 | 86 | 87 | if __name__ == '__main__': 88 | main() 89 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import platform 5 | import subprocess 6 | 7 | from distutils.version import LooseVersion 8 | from setuptools import setup, find_packages, Extension 9 | from setuptools.command.build_ext import build_ext 10 | 11 | 12 | class CMakeExtension(Extension): 13 | def __init__(self, name, sourcedir=''): 14 | Extension.__init__(self, name, sources=[]) 15 | self.sourcedir = os.path.abspath(sourcedir) 16 | 17 | 18 | class CMakeBuild(build_ext): 19 | def run(self): 20 | try: 21 | out = subprocess.check_output(['cmake', '--version']) 22 | except OSError: 23 | raise RuntimeError( 24 | "CMake must be installed to build the following extensions: " + 25 | ", ".join(e.name for e in self.extensions)) 26 | 27 | if platform.system() == "Windows": 28 | cmake_version = LooseVersion(re.search(r'version\s*([\d.]+)', 29 | out.decode()).group(1)) 30 | if cmake_version < '3.1.0': 31 | raise RuntimeError("CMake >= 3.1.0 is required on Windows") 32 | 33 | for ext in self.extensions: 34 | self.build_extension(ext) 35 | 36 | def build_extension(self, ext): 37 | extdir = os.path.abspath( 38 | os.path.dirname(self.get_ext_fullpath(ext.name))) 39 | cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, 40 | '-DPYTHON_EXECUTABLE=' + sys.executable] 41 | 42 | cfg = 'Debug' if self.debug else 'Release' 43 | build_args = ['--config', cfg] 44 | 45 | if platform.system() == "Windows": 46 | cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format( 47 | cfg.upper(), 48 | extdir)] 49 | if sys.maxsize > 2**32: 50 | cmake_args += ['-A', 'x64'] 51 | build_args += ['--', '/m'] 52 | else: 53 | cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] 54 | build_args += ['--', '-j2'] 55 | 56 | env = os.environ.copy() 57 | env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format( 58 | env.get('CXXFLAGS', ''), 59 | self.distribution.get_version()) 60 | if not os.path.exists(self.build_temp): 61 | os.makedirs(self.build_temp) 62 | subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, 63 | cwd=self.build_temp, env=env) 64 | subprocess.check_call(['cmake', '--build', '.'] + build_args, 65 | cwd=self.build_temp) 66 | print() # Add an empty line for cleaner output 67 | 68 | setup( 69 | name='mesher', 70 | version='0.1', 71 | author='Robert Smallshire', 72 | author_email='rob@sixty-north.com', 73 | description='Triangular meshes using CGAL', 74 | long_description='', 75 | packages=find_packages('src'), 76 | package_dir={'':'src'}, 77 | ext_modules=[CMakeExtension('mesher/cgal_mesher')], 78 | cmdclass=dict(build_ext=CMakeBuild), 79 | zip_safe=False, 80 | ) 81 | -------------------------------------------------------------------------------- /examples/triangulate9.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import ( 3 | Point, Mesher, make_conforming_delaunay, make_conforming_gabriel, Criteria 4 | ) 5 | 6 | def main(): 7 | cdt = CDT() 8 | 9 | va = cdt.insert(Point(100, 269)) 10 | vb = cdt.insert(Point(246, 269)) 11 | vc = cdt.insert(Point(246, 223)) 12 | vd = cdt.insert(Point(303, 223)) 13 | ve = cdt.insert(Point(303, 298)) 14 | vf = cdt.insert(Point(246, 298)) 15 | vg = cdt.insert(Point(246, 338)) 16 | vh = cdt.insert(Point(355, 338)) 17 | vi = cdt.insert(Point(355, 519)) 18 | vj = cdt.insert(Point(551, 519)) 19 | vk = cdt.insert(Point(551, 445)) 20 | vl = cdt.insert(Point(463, 445)) 21 | vm = cdt.insert(Point(463, 377)) 22 | vn = cdt.insert(Point(708, 377)) 23 | vo = cdt.insert(Point(708, 229)) 24 | vp = cdt.insert(Point(435, 229)) 25 | vq = cdt.insert(Point(435, 100)) 26 | vr = cdt.insert(Point(100, 100)) 27 | 28 | cdt.insert_constraint(va, vb) 29 | cdt.insert_constraint(vb, vc) 30 | cdt.insert_constraint(vc, vd) 31 | cdt.insert_constraint(vd, ve) 32 | cdt.insert_constraint(ve, vf) 33 | cdt.insert_constraint(vf, vg) 34 | cdt.insert_constraint(vg, vh) 35 | cdt.insert_constraint(vh, vi) 36 | cdt.insert_constraint(vi, vj) 37 | cdt.insert_constraint(vj, vk) 38 | cdt.insert_constraint(vk, vl) 39 | cdt.insert_constraint(vl, vm) 40 | cdt.insert_constraint(vm, vn) 41 | cdt.insert_constraint(vn, vo) 42 | cdt.insert_constraint(vo, vp) 43 | cdt.insert_constraint(vp, vq) 44 | cdt.insert_constraint(vq, vr) 45 | cdt.insert_constraint(vr, va) 46 | 47 | vs = cdt.insert(Point(349, 236)) 48 | vt = cdt.insert(Point(370, 236)) 49 | vu = cdt.insert(Point(370, 192)) 50 | vv = cdt.insert(Point(403, 192)) 51 | vw = cdt.insert(Point(403, 158)) 52 | vx = cdt.insert(Point(349, 158)) 53 | 54 | cdt.insert_constraint(vs, vt) 55 | cdt.insert_constraint(vt, vu) 56 | cdt.insert_constraint(vu, vv) 57 | cdt.insert_constraint(vv, vw) 58 | cdt.insert_constraint(vw, vx) 59 | cdt.insert_constraint(vx, vs) 60 | 61 | vy = cdt.insert(Point(501, 336)) 62 | vz = cdt.insert(Point(533, 336)) 63 | v1 = cdt.insert(Point(519, 307)) 64 | v2 = cdt.insert(Point(484, 307)) 65 | 66 | cdt.insert_constraint(vy, vz) 67 | cdt.insert_constraint(vz, v1) 68 | cdt.insert_constraint(v1, v2) 69 | cdt.insert_constraint(v2, vy) 70 | 71 | print("Number of vertices:", cdt.number_of_vertices()) 72 | 73 | mesher = Mesher(cdt) 74 | seeds = [ 75 | Point(505, 325), 76 | Point(379, 172), 77 | ] 78 | mesher.seeds_from(seeds) 79 | 80 | make_conforming_delaunay(cdt) 81 | print("Number of vertices:", cdt.number_of_vertices()) 82 | 83 | make_conforming_gabriel(cdt) 84 | print("Number of vertices:", cdt.number_of_vertices()) 85 | 86 | mesher.criteria = Criteria( 87 | aspect_bound=0.125, 88 | size_bound=30 89 | ) 90 | mesher.refine_mesh() 91 | print("Number of vertices:", cdt.number_of_vertices()) 92 | 93 | if __name__ == '__main__': 94 | main() 95 | -------------------------------------------------------------------------------- /examples/triangulate10.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import ( 3 | Point, Mesher, make_conforming_delaunay, make_conforming_gabriel, Criteria, lloyd_optimize 4 | ) 5 | 6 | def main(): 7 | cdt = CDT() 8 | 9 | va = cdt.insert(Point(100, 269)) 10 | vb = cdt.insert(Point(246, 269)) 11 | vc = cdt.insert(Point(246, 223)) 12 | vd = cdt.insert(Point(303, 223)) 13 | ve = cdt.insert(Point(303, 298)) 14 | vf = cdt.insert(Point(246, 298)) 15 | vg = cdt.insert(Point(246, 338)) 16 | vh = cdt.insert(Point(355, 338)) 17 | vi = cdt.insert(Point(355, 519)) 18 | vj = cdt.insert(Point(551, 519)) 19 | vk = cdt.insert(Point(551, 445)) 20 | vl = cdt.insert(Point(463, 445)) 21 | vm = cdt.insert(Point(463, 377)) 22 | vn = cdt.insert(Point(708, 377)) 23 | vo = cdt.insert(Point(708, 229)) 24 | vp = cdt.insert(Point(435, 229)) 25 | vq = cdt.insert(Point(435, 100)) 26 | vr = cdt.insert(Point(100, 100)) 27 | 28 | cdt.insert_constraint(va, vb) 29 | cdt.insert_constraint(vb, vc) 30 | cdt.insert_constraint(vc, vd) 31 | cdt.insert_constraint(vd, ve) 32 | cdt.insert_constraint(ve, vf) 33 | cdt.insert_constraint(vf, vg) 34 | cdt.insert_constraint(vg, vh) 35 | cdt.insert_constraint(vh, vi) 36 | cdt.insert_constraint(vi, vj) 37 | cdt.insert_constraint(vj, vk) 38 | cdt.insert_constraint(vk, vl) 39 | cdt.insert_constraint(vl, vm) 40 | cdt.insert_constraint(vm, vn) 41 | cdt.insert_constraint(vn, vo) 42 | cdt.insert_constraint(vo, vp) 43 | cdt.insert_constraint(vp, vq) 44 | cdt.insert_constraint(vq, vr) 45 | cdt.insert_constraint(vr, va) 46 | 47 | vs = cdt.insert(Point(349, 236)) 48 | vt = cdt.insert(Point(370, 236)) 49 | vu = cdt.insert(Point(370, 192)) 50 | vv = cdt.insert(Point(403, 192)) 51 | vw = cdt.insert(Point(403, 158)) 52 | vx = cdt.insert(Point(349, 158)) 53 | 54 | cdt.insert_constraint(vs, vt) 55 | cdt.insert_constraint(vt, vu) 56 | cdt.insert_constraint(vu, vv) 57 | cdt.insert_constraint(vv, vw) 58 | cdt.insert_constraint(vw, vx) 59 | cdt.insert_constraint(vx, vs) 60 | 61 | vy = cdt.insert(Point(501, 336)) 62 | vz = cdt.insert(Point(533, 336)) 63 | v1 = cdt.insert(Point(519, 307)) 64 | v2 = cdt.insert(Point(484, 307)) 65 | 66 | cdt.insert_constraint(vy, vz) 67 | cdt.insert_constraint(vz, v1) 68 | cdt.insert_constraint(v1, v2) 69 | cdt.insert_constraint(v2, vy) 70 | 71 | print("Number of vertices:", cdt.number_of_vertices()) 72 | 73 | mesher = Mesher(cdt) 74 | seeds = [ 75 | Point(505, 325), 76 | Point(379, 172), 77 | ] 78 | mesher.seeds_from(seeds) 79 | 80 | make_conforming_delaunay(cdt) 81 | print("Number of vertices:", cdt.number_of_vertices()) 82 | 83 | make_conforming_gabriel(cdt) 84 | print("Number of vertices:", cdt.number_of_vertices()) 85 | 86 | mesher.criteria = Criteria( 87 | aspect_bound=0.125, 88 | size_bound=30 89 | ) 90 | mesher.refine_mesh() 91 | print("Number of vertices:", cdt.number_of_vertices()) 92 | 93 | lloyd_optimize(cdt, max_iteration_number=10) 94 | print("Number of vertices:", cdt.number_of_vertices()) 95 | 96 | 97 | if __name__ == '__main__': 98 | main() 99 | -------------------------------------------------------------------------------- /examples/example_final.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import ( 3 | Point, 4 | make_conforming_delaunay, 5 | make_conforming_gabriel, 6 | Mesher, 7 | Criteria, 8 | lloyd_optimize, 9 | ) 10 | 11 | def main(): 12 | cdt = CDT() 13 | 14 | va = cdt.insert(Point(100, 269)) 15 | vb = cdt.insert(Point(246, 269)) 16 | vc = cdt.insert(Point(246, 223)) 17 | vd = cdt.insert(Point(303, 223)) 18 | ve = cdt.insert(Point(303, 298)) 19 | vf = cdt.insert(Point(246, 298)) 20 | vg = cdt.insert(Point(246, 338)) 21 | vh = cdt.insert(Point(355, 338)) 22 | vi = cdt.insert(Point(355, 519)) 23 | vj = cdt.insert(Point(551, 519)) 24 | vk = cdt.insert(Point(551, 445)) 25 | vl = cdt.insert(Point(463, 445)) 26 | vm = cdt.insert(Point(463, 377)) 27 | vn = cdt.insert(Point(708, 377)) 28 | vo = cdt.insert(Point(708, 229)) 29 | vp = cdt.insert(Point(435, 229)) 30 | vq = cdt.insert(Point(435, 100)) 31 | vr = cdt.insert(Point(100, 100)) 32 | 33 | cdt.insert_constraint(va, vb) 34 | cdt.insert_constraint(vb, vc) 35 | cdt.insert_constraint(vc, vd) 36 | cdt.insert_constraint(vd, ve) 37 | cdt.insert_constraint(ve, vf) 38 | cdt.insert_constraint(vf, vg) 39 | cdt.insert_constraint(vg, vh) 40 | cdt.insert_constraint(vh, vi) 41 | cdt.insert_constraint(vi, vj) 42 | cdt.insert_constraint(vj, vk) 43 | cdt.insert_constraint(vk, vl) 44 | cdt.insert_constraint(vl, vm) 45 | cdt.insert_constraint(vm, vn) 46 | cdt.insert_constraint(vn, vo) 47 | cdt.insert_constraint(vo, vp) 48 | cdt.insert_constraint(vp, vq) 49 | cdt.insert_constraint(vq, vr) 50 | cdt.insert_constraint(vr, va) 51 | 52 | vs = cdt.insert(Point(349, 236)) 53 | vt = cdt.insert(Point(370, 236)) 54 | vu = cdt.insert(Point(370, 192)) 55 | vv = cdt.insert(Point(403, 192)) 56 | vw = cdt.insert(Point(403, 158)) 57 | vx = cdt.insert(Point(349, 158)) 58 | 59 | cdt.insert_constraint(vs, vt) 60 | cdt.insert_constraint(vt, vu) 61 | cdt.insert_constraint(vu, vv) 62 | cdt.insert_constraint(vv, vw) 63 | cdt.insert_constraint(vw, vx) 64 | cdt.insert_constraint(vx, vs) 65 | 66 | vy = cdt.insert(Point(501, 336)) 67 | vz = cdt.insert(Point(533, 336)) 68 | v1 = cdt.insert(Point(519, 307)) 69 | v2 = cdt.insert(Point(484, 307)) 70 | 71 | cdt.insert_constraint(vy, vz) 72 | cdt.insert_constraint(vz, v1) 73 | cdt.insert_constraint(v1, v2) 74 | cdt.insert_constraint(v2, vy) 75 | 76 | print("Number of vertices:", cdt.number_of_vertices()) 77 | 78 | make_conforming_delaunay(cdt) 79 | 80 | print("Number of vertices:", cdt.number_of_vertices()) 81 | 82 | make_conforming_gabriel(cdt) 83 | 84 | print("Number of vertices:", cdt.number_of_vertices()) 85 | 86 | print("Meshing...") 87 | mesher = Mesher(cdt) 88 | mesher.criteria = Criteria( 89 | aspect_bound=0.125, 90 | size_bound=30 91 | ) 92 | mesher.refine_mesh() 93 | 94 | print("Number of vertices:", cdt.number_of_vertices()) 95 | 96 | lloyd_optimize(cdt, max_iteration_number=10) 97 | print("Number of vertices:", cdt.number_of_vertices()) 98 | 99 | 100 | if __name__ == '__main__': 101 | main() -------------------------------------------------------------------------------- /examples/triangulate11.py: -------------------------------------------------------------------------------- 1 | from mesher.cgal_mesher import ConstrainedDelaunayTriangulation as CDT 2 | from mesher.cgal_mesher import ( 3 | Point, Mesher, make_conforming_delaunay, make_conforming_gabriel, Criteria, lloyd_optimize 4 | ) 5 | 6 | def main(): 7 | cdt = CDT() 8 | 9 | va = cdt.insert(Point(100, 269)) 10 | vb = cdt.insert(Point(246, 269)) 11 | vc = cdt.insert(Point(246, 223)) 12 | vd = cdt.insert(Point(303, 223)) 13 | ve = cdt.insert(Point(303, 298)) 14 | vf = cdt.insert(Point(246, 298)) 15 | vg = cdt.insert(Point(246, 338)) 16 | vh = cdt.insert(Point(355, 338)) 17 | vi = cdt.insert(Point(355, 519)) 18 | vj = cdt.insert(Point(551, 519)) 19 | vk = cdt.insert(Point(551, 445)) 20 | vl = cdt.insert(Point(463, 445)) 21 | vm = cdt.insert(Point(463, 377)) 22 | vn = cdt.insert(Point(708, 377)) 23 | vo = cdt.insert(Point(708, 229)) 24 | vp = cdt.insert(Point(435, 229)) 25 | vq = cdt.insert(Point(435, 100)) 26 | vr = cdt.insert(Point(100, 100)) 27 | 28 | cdt.insert_constraint(va, vb) 29 | cdt.insert_constraint(vb, vc) 30 | cdt.insert_constraint(vc, vd) 31 | cdt.insert_constraint(vd, ve) 32 | cdt.insert_constraint(ve, vf) 33 | cdt.insert_constraint(vf, vg) 34 | cdt.insert_constraint(vg, vh) 35 | cdt.insert_constraint(vh, vi) 36 | cdt.insert_constraint(vi, vj) 37 | cdt.insert_constraint(vj, vk) 38 | cdt.insert_constraint(vk, vl) 39 | cdt.insert_constraint(vl, vm) 40 | cdt.insert_constraint(vm, vn) 41 | cdt.insert_constraint(vn, vo) 42 | cdt.insert_constraint(vo, vp) 43 | cdt.insert_constraint(vp, vq) 44 | cdt.insert_constraint(vq, vr) 45 | cdt.insert_constraint(vr, va) 46 | 47 | vs = cdt.insert(Point(349, 236)) 48 | vt = cdt.insert(Point(370, 236)) 49 | vu = cdt.insert(Point(370, 192)) 50 | vv = cdt.insert(Point(403, 192)) 51 | vw = cdt.insert(Point(403, 158)) 52 | vx = cdt.insert(Point(349, 158)) 53 | 54 | cdt.insert_constraint(vs, vt) 55 | cdt.insert_constraint(vt, vu) 56 | cdt.insert_constraint(vu, vv) 57 | cdt.insert_constraint(vv, vw) 58 | cdt.insert_constraint(vw, vx) 59 | cdt.insert_constraint(vx, vs) 60 | 61 | vy = cdt.insert(Point(501, 336)) 62 | vz = cdt.insert(Point(533, 336)) 63 | v1 = cdt.insert(Point(519, 307)) 64 | v2 = cdt.insert(Point(484, 307)) 65 | 66 | cdt.insert_constraint(vy, vz) 67 | cdt.insert_constraint(vz, v1) 68 | cdt.insert_constraint(v1, v2) 69 | cdt.insert_constraint(v2, vy) 70 | 71 | print("Number of vertices:", cdt.number_of_vertices()) 72 | 73 | mesher = Mesher(cdt) 74 | seeds = [ 75 | Point(505, 325), 76 | Point(379, 172), 77 | ] 78 | mesher.seeds_from(seeds) 79 | 80 | make_conforming_delaunay(cdt) 81 | print("Number of vertices:", cdt.number_of_vertices()) 82 | 83 | make_conforming_gabriel(cdt) 84 | print("Number of vertices:", cdt.number_of_vertices()) 85 | 86 | mesher.criteria = Criteria( 87 | aspect_bound=0.125, 88 | size_bound=30 89 | ) 90 | mesher.refine_mesh() 91 | print("Number of vertices:", cdt.number_of_vertices()) 92 | 93 | lloyd_optimize(cdt, max_iteration_number=10) 94 | print("Number of vertices:", cdt.number_of_vertices()) 95 | 96 | vertex_iterator = cdt.finite_vertices() 97 | print(next(vertex_iterator)) 98 | for vertex in cdt.finite_vertices(): 99 | print(vertex.point) 100 | 101 | for face in cdt.finite_faces(): 102 | for index in range(3): 103 | print(index, face.vertex_handle(index).point) 104 | 105 | if __name__ == '__main__': 106 | main() 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/clion,python,pycharm 3 | 4 | ### CLion ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/ 10 | 11 | # CMake 12 | cmake-build-*/ 13 | 14 | # Mongo Explorer plugin 15 | .idea/**/mongoSettings.xml 16 | 17 | # File-based project format 18 | *.iws 19 | 20 | # IntelliJ 21 | out/ 22 | 23 | # mpeltonen/sbt-idea plugin 24 | .idea_modules/ 25 | 26 | # JIRA plugin 27 | atlassian-ide-plugin.xml 28 | 29 | # Cursive Clojure plugin 30 | .idea/replstate.xml 31 | 32 | # Crashlytics plugin (for Android Studio and IntelliJ) 33 | com_crashlytics_export_strings.xml 34 | crashlytics.properties 35 | crashlytics-build.properties 36 | fabric.properties 37 | 38 | # Editor-based Rest Client 39 | .idea/httpRequests 40 | 41 | ### CLion Patch ### 42 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 43 | 44 | # *.iml 45 | # modules.xml 46 | # .idea/misc.xml 47 | # *.ipr 48 | 49 | # Sonarlint plugin 50 | .idea/sonarlint 51 | 52 | ### PyCharm ### 53 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 54 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 55 | 56 | # User-specific stuff 57 | 58 | # Sensitive or high-churn files 59 | 60 | # Gradle 61 | 62 | # Gradle and Maven with auto-import 63 | # When using Gradle or Maven with auto-import, you should exclude module files, 64 | # since they will be recreated, and may cause churn. Uncomment if using 65 | # auto-import. 66 | # .idea/modules.xml 67 | # .idea/*.iml 68 | # .idea/modules 69 | 70 | # CMake 71 | 72 | # Mongo Explorer plugin 73 | 74 | # File-based project format 75 | 76 | # IntelliJ 77 | 78 | # mpeltonen/sbt-idea plugin 79 | 80 | # JIRA plugin 81 | 82 | # Cursive Clojure plugin 83 | 84 | # Crashlytics plugin (for Android Studio and IntelliJ) 85 | 86 | # Editor-based Rest Client 87 | 88 | ### PyCharm Patch ### 89 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 90 | 91 | # *.iml 92 | # modules.xml 93 | # .idea/misc.xml 94 | # *.ipr 95 | 96 | # Sonarlint plugin 97 | 98 | ### Python ### 99 | # Byte-compiled / optimized / DLL files 100 | __pycache__/ 101 | *.py[cod] 102 | *$py.class 103 | 104 | # C extensions 105 | *.so 106 | 107 | # Distribution / packaging 108 | .Python 109 | build/ 110 | develop-eggs/ 111 | dist/ 112 | downloads/ 113 | eggs/ 114 | .eggs/ 115 | lib/ 116 | lib64/ 117 | parts/ 118 | sdist/ 119 | var/ 120 | wheels/ 121 | *.egg-info/ 122 | .installed.cfg 123 | *.egg 124 | MANIFEST 125 | 126 | # PyInstaller 127 | # Usually these files are written by a python script from a template 128 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 129 | *.manifest 130 | *.spec 131 | 132 | # Installer logs 133 | pip-log.txt 134 | pip-delete-this-directory.txt 135 | 136 | # Unit test / coverage reports 137 | htmlcov/ 138 | .tox/ 139 | .coverage 140 | .coverage.* 141 | .cache 142 | nosetests.xml 143 | coverage.xml 144 | *.cover 145 | .hypothesis/ 146 | .pytest_cache/ 147 | 148 | # Translations 149 | *.mo 150 | *.pot 151 | 152 | # Django stuff: 153 | *.log 154 | local_settings.py 155 | db.sqlite3 156 | 157 | # Flask stuff: 158 | instance/ 159 | .webassets-cache 160 | 161 | # Scrapy stuff: 162 | .scrapy 163 | 164 | # Sphinx documentation 165 | docs/_build/ 166 | 167 | # PyBuilder 168 | target/ 169 | 170 | # Jupyter Notebook 171 | .ipynb_checkpoints 172 | 173 | # pyenv 174 | .python-version 175 | 176 | # celery beat schedule file 177 | celerybeat-schedule 178 | 179 | # SageMath parsed files 180 | *.sage.py 181 | 182 | # Environments 183 | .env 184 | .venv 185 | env/ 186 | venv/ 187 | ENV/ 188 | env.bak/ 189 | venv.bak/ 190 | 191 | # Spyder project settings 192 | .spyderproject 193 | .spyproject 194 | 195 | # Rope project settings 196 | .ropeproject 197 | 198 | # mkdocs documentation 199 | /site 200 | 201 | # mypy 202 | .mypy_cache/ 203 | 204 | ### Python Patch ### 205 | .venv/ 206 | 207 | ### Python.VirtualEnv Stack ### 208 | # Virtualenv 209 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 210 | [Bb]in 211 | [Ii]nclude 212 | [Ll]ib 213 | [Ll]ib64 214 | [Ll]ocal 215 | [Ss]cripts 216 | pyvenv.cfg 217 | pip-selfcheck.json 218 | 219 | 220 | # End of https://www.gitignore.io/api/clion,python,pycharm 221 | 222 | 223 | # Created by https://www.gitignore.io/api/cmake 224 | 225 | ### CMake ### 226 | CMakeCache.txt 227 | CMakeFiles 228 | CMakeScripts 229 | Testing 230 | Makefile 231 | cmake_install.cmake 232 | install_manifest.txt 233 | compile_commands.json 234 | CTestTestfile.cmake 235 | 236 | 237 | # End of https://www.gitignore.io/api/cmake 238 | 239 | *.cbp 240 | DartConfiguration.tcl 241 | -------------------------------------------------------------------------------- /src/mesher/bindings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace py = pybind11; 22 | 23 | using K = CGAL::Exact_predicates_inexact_constructions_kernel; 24 | using Vb = CGAL::Delaunay_mesh_vertex_base_2; 25 | using Fb = CGAL::Delaunay_mesh_face_base_2; 26 | using Tds = CGAL::Triangulation_data_structure_2; 27 | using CDT = CGAL::Constrained_Delaunay_triangulation_2; 28 | using Criteria = CGAL::Delaunay_mesh_size_criteria_2; 29 | using Mesher = CGAL::Delaunay_mesher_2; 30 | 31 | using Point = CDT::Point; 32 | using Vertex_handle = CDT::Vertex_handle; 33 | 34 | template 35 | class TypedInputIterator 36 | { 37 | public: 38 | using iterator_category = std::input_iterator_tag; 39 | using difference_type = std::ptrdiff_t; 40 | using value_type = T; 41 | using pointer = T*; 42 | using reference = T&; 43 | 44 | explicit TypedInputIterator(py::iterator& py_iter) : 45 | py_iter_(py_iter) 46 | { 47 | } 48 | 49 | explicit TypedInputIterator(py::iterator&& py_iter) : 50 | py_iter_(py_iter) 51 | { 52 | } 53 | 54 | value_type operator*() 55 | { 56 | return (*py_iter_).template cast(); 57 | } 58 | 59 | TypedInputIterator operator++(int) 60 | { 61 | auto copy = *this; 62 | ++py_iter_; 63 | return copy; 64 | } 65 | 66 | TypedInputIterator& operator++() 67 | { 68 | ++py_iter_; 69 | return *this; 70 | } 71 | 72 | bool operator!=(TypedInputIterator &rhs) 73 | { 74 | return py_iter_ != rhs.py_iter_; 75 | } 76 | 77 | bool operator==(TypedInputIterator &rhs) 78 | { 79 | return py_iter_ == rhs.py_iter_; 80 | } 81 | 82 | private: 83 | py::iterator py_iter_; 84 | }; 85 | 86 | PYBIND11_MODULE(cgal_mesher, m) 87 | { 88 | py::class_(m, "Point") 89 | .def(py::init(), py::arg("x"), py::arg("y")) 90 | .def(py::init(), py::arg("x"), py::arg("y")) 91 | .def_property_readonly("x", &Point::x) 92 | .def_property_readonly("y", &Point::y) 93 | .def("__repr__", 94 | [](const Point &p) { 95 | std::string r("Point("); 96 | r += boost::lexical_cast(p.x()); 97 | r += ", "; 98 | r += boost::lexical_cast(p.y()); 99 | r += ")"; 100 | return r; 101 | }) 102 | ; 103 | 104 | py::class_(m, "VertexHandle"); 105 | 106 | py::class_(m, "ConstrainedDelaunayTriangulation") 107 | .def(py::init()) 108 | .def("insert", [](CDT & cdt, const Point & p) { return cdt.insert(p); }) 109 | .def("insert_constraint", 110 | [](CDT & cdt, Vertex_handle a, Vertex_handle b) 111 | { 112 | cdt.insert_constraint(a, b); 113 | }) 114 | .def("remove", &CDT::remove) 115 | .def("number_of_vertices", &CDT::number_of_vertices) 116 | .def("number_of_faces", &CDT::number_of_faces) 117 | .def("vertices", [](CDT & cdt) -> py::iterator 118 | { 119 | return py::make_iterator(cdt.vertices_begin(), cdt.vertices_end()); 120 | }) 121 | ; 122 | 123 | m.def("make_conforming_delaunay", &CGAL::make_conforming_Delaunay_2, 124 | py::arg("cdt") 125 | ); 126 | 127 | m.def("make_conforming_gabriel", &CGAL::make_conforming_Gabriel_2, 128 | py::arg("cdt") 129 | ); 130 | 131 | py::class_(m, "Criteria") 132 | .def(py::init(), 133 | py::arg("aspect_bound") = 0.125, 134 | py::arg("size_bound") = 0.0) 135 | .def_property("size_bound", &Criteria::size_bound, &Criteria::set_size_bound) 136 | .def_property("aspect_bound", 137 | [](const Criteria & c) { c.bound(); }, 138 | [](Criteria & c, double bound) { c.set_bound(bound); }) 139 | ; 140 | 141 | py::class_(m, "Mesher") 142 | .def(py::init()) 143 | .def("refine_mesh", &Mesher::refine_mesh) 144 | .def_property( 145 | "criteria", 146 | &Mesher::get_criteria, 147 | [](Mesher& mesher, const Criteria & criteria) 148 | { 149 | mesher.set_criteria(criteria); 150 | } 151 | ) 152 | .def_property_readonly("seeds", [](Mesher& mesher) { 153 | return py::make_iterator(mesher.seeds_begin(), mesher.seeds_end()); 154 | }) 155 | .def("seeds_from", [](Mesher & mesher, py::iterable iterable, bool mark) 156 | { 157 | py::iterator iterator = py::iter(iterable); 158 | TypedInputIterator points_begin(iterator); 159 | TypedInputIterator points_end(py::iterator::sentinel()); 160 | mesher.set_seeds(points_begin, points_end, mark); 161 | }) 162 | .def("clear_seeds", &Mesher::clear_seeds) 163 | ; 164 | 165 | 166 | m.def("lloyd_optimize", []( 167 | CDT& cdt, 168 | int max_iteration_number, 169 | double time_limit, 170 | double convergence, 171 | double freeze_bound) 172 | { 173 | CGAL::lloyd_optimize_mesh_2(cdt, 174 | CGAL::parameters::max_iteration_number = max_iteration_number, 175 | CGAL::parameters::time_limit = time_limit, 176 | CGAL::parameters::convergence = convergence, 177 | CGAL::parameters::freeze_bound = freeze_bound 178 | ); 179 | }, 180 | py::arg("cdt"), 181 | py::arg("max_iteration_number") = 0, 182 | py::arg("time_limit") = 0.0, 183 | py::arg("convergence") = 0.001, 184 | py::arg("freeze_bound") = 0.001 185 | ); 186 | } 187 | -------------------------------------------------------------------------------- /src/mesher/cgal_mesher.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | 16 | 17 | #include 18 | #include 19 | #ifndef _MSC_VER 20 | # include 21 | #endif 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | template struct hash; 29 | 30 | template 31 | struct hash 32 | : public std::hash 33 | { 34 | using std::hash::hash; 35 | }; 36 | 37 | 38 | template 39 | struct hash 40 | { 41 | inline std::size_t operator()(const T& v, const Rest&... rest) { 42 | std::size_t seed = hash{}(rest...); 43 | seed ^= hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 44 | return seed; 45 | } 46 | }; 47 | 48 | template 49 | std::string 50 | type_name() 51 | { 52 | typedef typename std::remove_reference::type TR; 53 | std::unique_ptr own 54 | ( 55 | nullptr, 56 | std::free 57 | ); 58 | std::string r = own != nullptr ? own.get() : typeid(TR).name(); 59 | if (std::is_const::value) 60 | r += " const"; 61 | if (std::is_volatile::value) 62 | r += " volatile"; 63 | if (std::is_lvalue_reference::value) 64 | r += "&"; 65 | else if (std::is_rvalue_reference::value) 66 | r += "&&"; 67 | return r; 68 | } 69 | 70 | namespace py = pybind11; 71 | 72 | using K = CGAL::Exact_predicates_inexact_constructions_kernel; 73 | using Vb = CGAL::Delaunay_mesh_vertex_base_2; 74 | using Fb = CGAL::Delaunay_mesh_face_base_2; 75 | using Tds = CGAL::Triangulation_data_structure_2; 76 | using CDT = CGAL::Constrained_Delaunay_triangulation_2; 77 | using Point = CDT::Point; 78 | using Vertex_handle = CDT::Vertex_handle; 79 | using Criteria = CGAL::Delaunay_mesh_size_criteria_2; 80 | using Mesher = CGAL::Delaunay_mesher_2; 81 | 82 | template 83 | class TypedInputIterator 84 | { 85 | public: 86 | using iterator_category = std::input_iterator_tag; 87 | using difference_type = std::ptrdiff_t; 88 | using value_type = T; 89 | using pointer = T*; 90 | using reference = T&; 91 | 92 | explicit TypedInputIterator(py::iterator& py_iter) : 93 | py_iter_(py_iter) 94 | { 95 | } 96 | 97 | explicit TypedInputIterator(py::iterator&& py_iter) : 98 | py_iter_(py_iter) 99 | { 100 | } 101 | 102 | value_type operator*() 103 | { 104 | return (*py_iter_).template cast(); 105 | } 106 | 107 | TypedInputIterator operator++(int) 108 | { 109 | auto copy = *this; 110 | ++py_iter_; 111 | return copy; 112 | } 113 | 114 | TypedInputIterator& operator++() 115 | { 116 | ++py_iter_; 117 | return *this; 118 | } 119 | 120 | bool operator!=(TypedInputIterator &rhs) 121 | { 122 | return py_iter_ != rhs.py_iter_; 123 | } 124 | 125 | bool operator==(TypedInputIterator &rhs) 126 | { 127 | return py_iter_ == rhs.py_iter_; 128 | } 129 | 130 | private: 131 | py::iterator py_iter_; 132 | }; 133 | 134 | PYBIND11_MODULE(cgal_mesher, m) 135 | { 136 | 137 | py::class_(m, "Point") 138 | .def(py::init(), py::arg("x"), py::arg("y")) 139 | .def(py::init(), py::arg("x"), py::arg("y")) 140 | .def_property_readonly("x", &Point::x) 141 | .def_property_readonly("y", &Point::y) 142 | .def("__repr__", 143 | [](const Point &p) { 144 | std::string r("Point("); 145 | r += boost::lexical_cast(p.x()); 146 | r += ", "; 147 | r += boost::lexical_cast(p.y()); 148 | r += ")"; 149 | return r; 150 | }) 151 | .def("__hash__", 152 | [](const Point &p) { 153 | std::hash double_hash; 154 | auto x_hash = double_hash(p.x()); 155 | auto y_hash = double_hash(p.y()); 156 | return y_hash ^ x_hash + 0x9e3779b9 + (y_hash << 6) + (y_hash >> 2); 157 | } 158 | ) 159 | .def("__eq__", 160 | [](const Point &p, const Point & q) { 161 | return p == q; 162 | } 163 | ) 164 | ; 165 | 166 | py::class_(m, "VertexHandle") 167 | .def_property_readonly( 168 | "point", 169 | [](const Vertex_handle& vertex_handle) 170 | { 171 | return vertex_handle->point(); 172 | }) 173 | ; 174 | 175 | py::class_(m, "Vertex") 176 | .def_property_readonly( 177 | "point", [](CDT::Finite_vertices_iterator::value_type& vertex) 178 | { 179 | return vertex.point(); 180 | } 181 | ) 182 | ; 183 | 184 | py::class_(m, "Face") 185 | .def("vertex_handle", 186 | [](CDT::Finite_faces_iterator::value_type& face, int index) 187 | { 188 | return face.vertex(index); 189 | }, 190 | py::arg("index") 191 | ) 192 | ; 193 | 194 | m.def("print_faces_iterator_value_type", [](){ 195 | std::cout << type_name(); 196 | }); 197 | 198 | py::class_(m, "ConstrainedDelaunayTriangulation") 199 | .def(py::init()) 200 | .def("insert", [](CDT & cdt, const Point & p) { return cdt.insert(p); }) 201 | .def("insert_constraint", 202 | [](CDT & cdt, Vertex_handle a, Vertex_handle b) 203 | { 204 | cdt.insert_constraint(a, b); 205 | }) 206 | .def("number_of_vertices", &CDT::number_of_vertices) 207 | .def("number_of_faces", &CDT::number_of_faces) 208 | .def("finite_vertices", [](CDT & cdt) -> py::iterator 209 | { 210 | return py::make_iterator(cdt.finite_vertices_begin(), cdt.finite_vertices_end()); 211 | }) 212 | .def("finite_faces", [](CDT & cdt) -> py::iterator 213 | { 214 | return py::make_iterator(cdt.finite_faces_begin(), cdt.finite_faces_end()); 215 | }) 216 | ; 217 | 218 | py::class_(m, "Criteria") 219 | .def(py::init(), 220 | py::arg("aspect_bound") = 0.125, 221 | py::arg("size_bound") = 0.0) 222 | .def_property("size_bound", &Criteria::size_bound, &Criteria::set_size_bound) 223 | .def_property("aspect_bound", 224 | [](const Criteria & c) { c.bound(); }, 225 | [](Criteria & c, double bound) { c.set_bound(bound); }) 226 | ; 227 | 228 | py::class_(m, "Mesher") 229 | .def(py::init()) 230 | .def("seeds_from", [](Mesher & mesher, py::iterable iterable) 231 | { 232 | py::iterator iterator = py::iter(iterable); 233 | TypedInputIterator points_begin(iterator); 234 | TypedInputIterator points_end(py::iterator::sentinel()); 235 | mesher.set_seeds(points_begin, points_end); 236 | }) 237 | .def("refine_mesh", &Mesher::refine_mesh) 238 | .def_property( 239 | "criteria", 240 | &Mesher::get_criteria, 241 | [](Mesher& mesher, const Criteria & criteria) 242 | { 243 | mesher.set_criteria(criteria); 244 | } 245 | ) 246 | ; 247 | 248 | m.def("make_conforming_delaunay", 249 | &CGAL::make_conforming_Delaunay_2, 250 | py::arg("cdt") 251 | ); 252 | 253 | m.def("make_conforming_gabriel", 254 | &CGAL::make_conforming_Gabriel_2, 255 | py::arg("cdt") 256 | ); 257 | 258 | m.def("lloyd_optimize", []( 259 | CDT& cdt, 260 | int max_iteration_number, 261 | double time_limit, 262 | double convergence, 263 | double freeze_bound) 264 | { 265 | CGAL::lloyd_optimize_mesh_2(cdt, 266 | CGAL::parameters::max_iteration_number = max_iteration_number, 267 | CGAL::parameters::time_limit = time_limit, 268 | CGAL::parameters::convergence = convergence, 269 | CGAL::parameters::freeze_bound = freeze_bound 270 | ); 271 | }, 272 | py::arg("cdt"), 273 | py::arg("max_iteration_number") = 0, 274 | py::arg("time_limit") = 0.0, 275 | py::arg("convergence") = 0.001, 276 | py::arg("freeze_bound") = 0.001 277 | ); 278 | 279 | } 280 | --------------------------------------------------------------------------------