Skip to content

Commit 34d569e

Browse files
authored
Cgal (#162)
* updated pybind11 * small setup cleanup * adds spherical sheet and monolayer generators from CGAL * bump version number
1 parent 2251ee5 commit 34d569e

File tree

10 files changed

+152
-20
lines changed

10 files changed

+152
-20
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ before_install:
1010
- export PATH="$HOME/miniconda/bin:$PATH"
1111
- hash -r
1212
- conda config --set always_yes yes --set changeps1 no
13-
- conda update -q conda
1413
- conda info -a
1514
- conda env create -q -f environment.yml
1615
- source activate tyssue

CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ endif()
2828
include( ${CGAL_USE_FILE})
2929

3030

31-
set (SOURCE_DIR "tyssue/collisions/cpp")
32-
include_directories (${SOURCE_DIR})
31+
set (COLLISION_SRCE "tyssue/collisions/cpp")
32+
set (GENERATION_SRCE "tyssue/generation/cpp")
33+
34+
include_directories (${COLLISION_SRCE} ${GENERATION_SRCE})
3335

3436
# Assumes pybind11 is a subdirectory at the project root
3537
# Make sure you git cloned tyssue recursively
3638
add_subdirectory(pybind11)
37-
pybind11_add_module(c_collisions ${SOURCES} "${SOURCE_DIR}/c_collisions.cpp" )
39+
pybind11_add_module(c_collisions ${SOURCES} "${COLLISION_SRCE}/c_collisions.cpp" )
40+
pybind11_add_module(mesh_generation ${SOURCES} "${GENERATION_SRCE}/mesh_generation.cpp" )

setup.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
## Thanks to them!
3030
MAJOR = 0
3131
MINOR = 6
32-
MICRO = 0
33-
ISRELEASED = True
32+
MICRO = 1
33+
ISRELEASED = False
3434
VERSION = "%d.%d.%s" % (MAJOR, MINOR, MICRO)
3535

3636

@@ -166,8 +166,7 @@ def build_extension(self, ext):
166166
subprocess.check_call(
167167
["cmake", "--build", "."] + build_args, cwd=self.build_temp
168168
)
169-
print(env["CXXFLAGS"])
170-
print() # Add an empty line for cleaner output
169+
print(env["CXXFLAGS"], "\n")
171170

172171

173172
if __name__ == "__main__":
@@ -199,7 +198,10 @@ def build_extension(self, ext):
199198
packages=find_packages(),
200199
package_data={"tyssue": files},
201200
include_package_data=True,
202-
ext_modules=[CMakeExtension("tyssue/collisions/cpp/c_collisions")],
201+
ext_modules=[
202+
CMakeExtension("tyssue/collisions/cpp/c_collisions"),
203+
CMakeExtension("tyssue/generation/cpp/mesh_generation"),
204+
],
203205
cmdclass=dict(build_ext=CMakeBuild),
204206
zip_safe=False,
205207
)

tests/generation/test_shapes.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55

66
import numpy as np
77
from numpy.testing import assert_almost_equal
8-
from tyssue.generation.shapes import generate_ring, ellipsoid_sheet, spherical_sheet
8+
from tyssue.generation.shapes import (
9+
generate_ring,
10+
ellipsoid_sheet,
11+
spherical_sheet,
12+
spherical_monolayer,
13+
)
914
from tyssue import PlanarGeometry
1015

1116

@@ -62,4 +67,21 @@ def test_ellipsoid():
6267
def test_spherical():
6368
sph = spherical_sheet(1.0, 40)
6469
rho = np.linalg.norm(sph.vert_df[sph.coords], axis=1)
65-
np.testing.assert_array_almost_equal(rho, np.ones(sph.Nv), decimal=1)
70+
np.testing.assert_almost_equal(rho.mean(), 1.0, decimal=1)
71+
72+
73+
def test_spherical_mono():
74+
radius = 1 # µm
75+
height = 3 # the height of the cells
76+
Nc = 400 # the (approximate) number of cells
77+
R_in = radius
78+
R_out = radius + height
79+
80+
organo = spherical_monolayer(R_in, R_out, Nc, apical="in")
81+
82+
rho = np.linalg.norm(organo.vert_df[organo.coords], axis=1)
83+
84+
mean_apical = rho[organo.vert_df["segment"] == "apical"].mean()
85+
mean_basal = rho[organo.vert_df["segment"] == "basal"].mean()
86+
np.testing.assert_almost_equal(mean_apical, radius, decimal=1)
87+
np.testing.assert_almost_equal(mean_basal, radius + height, decimal=1)

tyssue/generation/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
from .from_voronoi import *
77
from .modifiers import *
88
from .shapes import *
9+
from .cpp import mesh_generation

tyssue/generation/cpp/__init__.py

Whitespace-only changes.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <pybind11/pybind11.h>
2+
#include <pybind11/stl.h>
3+
#include <pybind11/complex.h>
4+
#include <pybind11/numpy.h>
5+
#include <boost/lexical_cast.hpp>
6+
7+
#include <fstream>
8+
#include <vector>
9+
#include <string>
10+
#include <sstream>
11+
#include <algorithm>
12+
#include <iostream>
13+
14+
15+
#include <CGAL/Surface_mesh_default_triangulation_3.h>
16+
#include <CGAL/Complex_2_in_triangulation_3.h>
17+
#include <CGAL/make_surface_mesh.h>
18+
#include <CGAL/Implicit_surface_3.h>
19+
20+
// default triangulation for Surface_mesher
21+
typedef CGAL::Surface_mesh_default_triangulation_3 Tr;
22+
// c2t3
23+
typedef CGAL::Complex_2_in_triangulation_3<Tr> C2t3;
24+
typedef Tr::Geom_traits GT;
25+
typedef GT::Sphere_3 Sphere_3;
26+
typedef GT::Point_3 Point_3;
27+
typedef GT::FT FT;
28+
typedef FT (*Function)(Point_3);
29+
typedef CGAL::Implicit_surface_3<GT, Function> Surface_3;
30+
//typedef C2t3::Vertex_handle vertex_descriptor;
31+
//using vertex_descriptor = boost::graph_traits<C2t3>::vertex_descriptor;
32+
33+
34+
FT sphere_function (Point_3 p) {
35+
const FT x2=p.x()*p.x(), y2=p.y()*p.y(), z2=p.z()*p.z();
36+
return x2+y2+z2-1;
37+
}
38+
39+
namespace py = pybind11;
40+
41+
std::vector<std::tuple<double, double, double>> make_spherical(double num_points) {
42+
Tr tr; // 3D-Delaunay triangulation
43+
C2t3 c2t3 (tr); // 2D-complex in 3D-Delaunay triangulation
44+
// defining the surface
45+
Surface_3 surface(sphere_function, // pointer to function
46+
Sphere_3(CGAL::ORIGIN, 2.)); // bounding sphere
47+
// Note that "2." above is the *squared* radius of the bounding sphere!
48+
// defining meshing criteria
49+
double r_max = std::sqrt(1 / (num_points * 0.12));
50+
CGAL::Surface_mesh_default_criteria_3<Tr> criteria(30, r_max, r_max);
51+
// meshing surface
52+
CGAL::make_surface_mesh(c2t3, surface, criteria, CGAL::Non_manifold_tag());
53+
std::vector<std::tuple<double, double, double>> points;
54+
Tr::Finite_vertices_iterator it;
55+
for (it = tr.finite_vertices_begin(); it != tr.finite_vertices_end(); it++)
56+
{
57+
Point_3 p = tr.point(it);
58+
std::tuple<double, double, double> tmp = std::make_tuple(p.x(), p.y(), p.z());
59+
points.push_back(tmp);
60+
}
61+
return points;
62+
}
63+
64+
65+
PYBIND11_MODULE(mesh_generation, m)
66+
{
67+
m.def("make_spherical", &make_spherical);
68+
}

tyssue/generation/shapes.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@
99
from ..core.sheet import Sheet, get_outer_sheet
1010
from ..core.objects import get_prev_edges
1111
from ..core.objects import Epithelium
12+
from ..core.monolayer import Monolayer
13+
1214
from ..topology import type1_transition
1315
from .from_voronoi import from_3d_voronoi
14-
from ..geometry.bulk_geometry import BulkGeometry
15-
from ..geometry.sheet_geometry import EllipsoidGeometry, SheetGeometry
16-
17-
from ..utils import single_cell
16+
from ..geometry.bulk_geometry import BulkGeometry, ClosedMonolayerGeometry
17+
from ..geometry.sheet_geometry import (
18+
EllipsoidGeometry,
19+
SheetGeometry,
20+
ClosedSheetGeometry,
21+
)
22+
from .cpp import mesh_generation
23+
from .modifiers import extrude
24+
from ..utils import single_cell, swap_apico_basal
1825

1926

2027
class AnnularSheet(Sheet):
@@ -272,13 +279,32 @@ def spherical_sheet(radius, Nf, **kwargs):
272279
the given number of cells
273280
"""
274281

275-
n_zs = int(np.ceil(np.roots([2, 1.0, -Nf])[-1])) # determined experimentaly ;p
276-
eptm = ellipsoid_sheet(radius, radius, radius, n_zs, **kwargs)
277-
eptm.settings.pop("abc")
278-
eptm.settings["radius"] = radius
282+
centers = np.array(mesh_generation.make_spherical(Nf))
283+
eptm = sheet_from_cell_centers(centers, **kwargs)
284+
285+
rhos = (eptm.vert_df[eptm.coords] ** 2).sum(axis=1).mean()
286+
ClosedSheetGeometry.scale(eptm, radius / rhos, eptm.coords)
287+
288+
ClosedSheetGeometry.update_all(eptm)
279289
return eptm
280290

281291

292+
def spherical_monolayer(R_in, R_out, Nc, apical="out"):
293+
"""Returns a spherical monolayer with the given inner and
294+
outer radii, and approximately the gieven number of cells.
295+
296+
The `apical` argument can be 'in' out 'out' to specify wether
297+
the apical face of the cells faces inward or outward, reespectively.
298+
"""
299+
sheet = spherical_sheet(R_in, Nc)
300+
delta_R = R_out - R_in
301+
mono = Monolayer("mono", extrude(sheet.datasets, method="normals", scale=-delta_R))
302+
if apical == "out":
303+
swap_apico_basal(mono)
304+
ClosedMonolayerGeometry.update_all(mono)
305+
return mono
306+
307+
282308
def sheet_from_cell_centers(points, noise=0):
283309
"""Returns a Sheet object from the Voronoï tessalation
284310
of the cell centers.

tyssue/utils/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,14 @@ def get_next(eptm):
314314
)
315315
next_ = fs_indexed.loc[ft_index, "edge"].values
316316
return next_
317+
318+
319+
## small utlity to swap apical and basal segments
320+
def swap_apico_basal(organo):
321+
"""Swap apical and basal segments of an organoid
322+
"""
323+
for elem in ["vert", "face", "edge"]:
324+
swaped = organo.datasets[elem]["segment"].copy()
325+
swaped.loc[organo.segment_index("apical", elem)] = "basal"
326+
swaped.loc[organo.segment_index("basal", elem)] = "apical"
327+
organo.datasets[elem]["segment"] = swaped

0 commit comments

Comments
 (0)