Skip to content

The mesh

The mesh every inference reads: DcelData, a multimaterial triangle mesh as a doubly connected edge list.

A DcelData holds the vertices, the triangles with the two materials each separates, and the half-edge structure the geometry (foambryo.geometry, foambryo.curvature) walks. Build one with foambryo.dcel_mesh_from_segmentation_mask, foambryo.dcel_mesh_from_file or foambryo.dcel_mesh_from_multimaterial_mesh.

Sacha Ichbiah, 2021. Matthieu Perez, 2024.

Vertex dataclass

Vertex(x: float = 0.0, y: float = 0.0, z: float = 0.0, key: int = 0)

Vertex in 2D.

HalfEdge dataclass

HalfEdge(origin: Vertex = None, destination: Vertex = None, material_1: int = 0, material_2: int = 0, twin: HalfEdge = None, incident_face: Face = None, prev_he: HalfEdge = None, next_he: HalfEdge = None, attached: dict = dict(), key: int = 0)

Half-Edge of a DCEL graph.

compute_length

compute_length() -> None

Compute length of edge. Keep value in self.length.

set_face

set_face(face: Face) -> None

Set incident face of this edge.

set_prev

set_prev(other: HalfEdge) -> None

Set previous HalfEdge.

set_next

set_next(other: HalfEdge) -> None

Set next HalfEdge.

set_twin

set_twin(other: HalfEdge) -> None

Set twin HalfEdge.

return_vector

return_vector() -> NDArray[np.float64]

Return a normalized vector from origin to destination.

Face dataclass

Face(attached: dict = dict(), outer_component: HalfEdge = None, _closed: bool = True, material_1: int = 0, material_2: int = 0, key: int = 0)

Face of a DCEL graph.

first_half_edge

first_half_edge() -> HalfEdge | None

Get the first HalfEdge of the face.

last_half_edge

last_half_edge() -> HalfEdge | None

Get the last HalfEdge of the face.

closed

closed() -> bool

Whether the face is closed or not (can we loop throught HalfEdges ?).

get_edges

get_edges() -> list[HalfEdge]

Get the ordered list of HalfEdges forming the face.

get_materials

get_materials() -> None

Set the materials of the face from its outer component HalfEdge.

get_vertices

get_vertices() -> list[Vertex]

Get the list of Vertices forming the Face.

get_area

get_area() -> float | None

Get area of face. None if face is not closed.

DcelData

DcelData(points: NDArray[float64], triangles: NDArray[int64], labels: NDArray[int64], *, crop_faces: NDArray[bool_] | NDArray[integer] | None = None, source_spacing: tuple[float, float, float] | None = None)

A multimaterial triangle mesh as a doubly connected edge list (DCEL): faces, half-edges and vertices.

Conventions: label 0 is the exterior medium; every triangle's labels are stored as material_1 < material_2, its winding flipped to match (_normalize_label_order), so an interface is keyed (label_1, label_2) with label_1 < label_2; coordinates are in the mesh's own length unit (voxels, or the unit of the spacing the mesh was reconstructed with).

Attributes:

Name Type Description
v NDArray[float64]

vertex positions, (nv, 3), unused vertices removed.

f NDArray[int64]

triangles and labels, (nf, 5) rows (v0, v1, v2, material_1, material_2), in the input's row order.

materials NDArray[int64]

the labels present, sorted (0 first when the medium is).

n_materials int

their number.

crop_faces NDArray[bool_]

(nf,), True on the crop faces (all False without).

source_spacing tuple[float, float, float] | None

the source mask's voxel size, if recorded.

vertices list[Vertex]

the DCEL's vertex records, aligned with v.

half_edges list[HalfEdge]

the DCEL's half-edge records.

faces list[Face]

the DCEL's face records, aligned with the rows of f.

Take a multimaterial mesh as input.

Parameters:

Name Type Description Default
points NDArray[float64]

Mesh vertex positions, shape (nv, 3).

required
triangles NDArray[int64]

Triangles as vertex indices, shape (nf, 3).

required
labels NDArray[int64]

The two materials each triangle separates, shape (nf, 2). A triangle's stored winding must put its right-hand-rule normal on the second label's side, i.e. point outward from the first — see _normalize_label_order for why, and for what goes wrong silently when a mesh from an outside source violates it.

required
crop_faces NDArray | None

which triangles are crop faces: faces on the boundary of the imaged volume, where the tissue was cut by the crop rather than bounded by the medium. A boolean mask of shape (nf,) or the row indices of the crop faces, aligned with triangles. Keyword-only. Defaults to None, no crop face. A crop face keeps its labels (0, c); every inference method keeps it, and every junction edge and vertex that touches it, out of its equations, leaves the interfaces made only of crop faces out of its tension map, and reports the pressure of a cut cell as nan. See foambryo.crop_faces.

None
source_spacing tuple[float, float, float] | None

the voxel size of the mask the mesh was made from, along the mask's axes 0, 1, 2, when the mesh records it: delaunay-watershed-3d's construct_mesh_in_physical_units writes it in the .rec file, dcel_mesh_from_file reads it, and dcel_mesh_from_segmentation_mask sets it when given a spacing. Keyword-only. Defaults to None, unknown. It changes nothing: the mesh is already in physical units, and no inference reads it. It is kept so that a mesh says which voxels it came from.

None

Raises:

Type Description
ValueError

If any triangle carries the same material on both sides, or if crop_faces is not a mask or a set of rows of triangles, or names a triangle that does not carry the medium label 0, or if source_spacing is not three positive finite numbers.

source_anisotropy property

source_anisotropy: float | None

Longest over shortest source voxel size (1.0 for cubic voxels); None when the spacing is unknown.

has_crop_faces property

has_crop_faces: bool

Whether any triangle is a crop face. When False, no inference method applies a crop-face rule.

crop_vertices property

crop_vertices: NDArray[int64]

Sorted keys of the vertices that lie on a crop face (empty without crop faces).

cut_cells property

cut_cells: tuple[int, ...]

The cells the crop cuts: a cell with a crop face or with a vertex on one, sorted.

Their volume and part of their surface lie outside the imaged volume, so a pressure inferred for them is not a measurement: the pressure methods report it as nan.

crop_interfaces property

crop_interfaces: tuple[tuple[int, int], ...]

Interfaces (0, c) all of whose triangles are crop faces, sorted.

They are the crop itself, not a contact between a cell and the medium, so they carry no tension: no inference method gives them an unknown's value, and the tension maps leave them out. An interface (0, c) with crop faces and real contact with the medium keeps its tension, which then describes the real contact only.

compute_scattered_arrays

compute_scattered_arrays(coeff: float) -> None

Modify the mesh to obtain the alternative "scattered" mesh, with each region separated from others.

coeff = 0 means no displacement.

compute_length_halfedges

compute_length_halfedges() -> None

Compute all lengths of HalfEdges.

compute_areas_faces

compute_areas_faces() -> None

Compute the area of every triangle using Heron's formula.

compute_vertex_normals

compute_vertex_normals() -> NDArray[np.float64]

Compute normals at every vertex, from the mesh's own points and triangles.

Returns:

Type Description
NDArray[float64]

NDArray[np.float64]: normal for every vertex

compute_verts_faces_interfaces

compute_verts_faces_interfaces() -> tuple[dict[tuple[int, int], NDArray[np.float64]], dict[tuple[int, int], NDArray[np.int64]]]

Returns two dicts that maps (label1, label2) of an interface to array of vertices & triangles.

compute_networkx_graph

compute_networkx_graph() -> networkx.Graph

Compute a graph with values extracted from the mesh. Nodes = regions, edges = interfaces.

find_trijunctional_edges

find_trijunctional_edges() -> NDArray[np.int64]

Return an array of edges on trijunction.

Each edge is [index of point 1, index of point 2].

compute_centroids_cells

compute_centroids_cells() -> None

Compute internally an array of each region's centroid.

mark_trijunctional_vertices

mark_trijunctional_vertices(return_list: bool = False) -> NDArray[np.int64]

Check for vertices on trijunction and mark them as such. Can return the keys of those vertices.

compute_length_trijunctions

compute_length_trijunctions(prints: bool = False) -> dict[tuple[int, int, int], float]

Compute the length of trijunctions, identified by their 3 adjacent regions.

compute_areas_cells

compute_areas_cells() -> dict[int, float]

Compute the area of each region.

compute_areas_interfaces

compute_areas_interfaces() -> dict[tuple[int, int], float]

Compute area of every interface (label1, label2) in mesh.

compute_area_derivatives_slow

compute_area_derivatives_slow() -> dict[tuple[int, int], NDArray[np.float64]]

Compute dict that maps interface (label1, label2) to array of change of area per point.

Alias of :meth:compute_area_derivatives, kept for backward compatibility. Both return the closed-form (float64) derivative — the _slow name predates that being the default.

compute_area_derivatives

compute_area_derivatives() -> dict[tuple[int, int], NDArray[np.float64]]

Compute dict that maps interface (label1, label2) to array of change of area per point.

Closed-form float64 derivative (the current default; the former float32 autodiff path lives in :mod:foambryo._autodiff_derivatives as a gradient check). The variational solve itself uses the sparse operator :func:foambryo.geometry.compute_area_derivative_sparse; this dict form is kept for callers that want a per-interface field (e.g. viewing).

compute_volumes_cells

compute_volumes_cells() -> dict[int, float]

Compute map cell number -> volume.

compute_volume_derivatives_slow

compute_volume_derivatives_slow() -> dict[int, NDArray[np.float64]]

Compute map cell number -> derivative of volume wrt to each point.

Alias of :meth:compute_volume_derivatives, kept for backward compatibility (see :meth:compute_area_derivatives_slow).

compute_volume_derivatives

compute_volume_derivatives() -> dict[int, NDArray[np.float64]]

Compute map cell number -> derivative of volume wrt to each point.

Closed-form float64 derivative (the current default; see :meth:compute_area_derivatives).

compute_length_derivatives

compute_length_derivatives() -> dict[tuple[int, int, int], NDArray[np.float64]]

Compute map trijunction (id reg 1, id reg 2, id reg 3) -> change of length wrt points.

Closed-form float64 derivative (the current default; the former autodiff path lives in :mod:foambryo._autodiff_derivatives).

compute_angles_junctions

compute_angles_junctions(unique: bool = True, *, reading: str = CHORD_READING) -> dict[tuple[int, int, int], float]

Compute a map trijunction (id reg 1, id reg 2, id reg 3) to mean angle in radians.

Parameters:

Name Type Description Default
unique bool

Count each geometric edge once. Defaults to True.

True
reading str

angle reading, one of foambryo.second_order_angle_reading.ANGLE_READINGS. Keyword-only. Defaults to "chord". See :func:foambryo.geometry.compute_angles_tri.

CHORD_READING

Returns:

Type Description
dict[tuple[int, int, int], float]

dict[tuple[int, int, int], float]: mean angle in radians, by wedge key.

compute_angles_tri

compute_angles_tri(unique: bool = True, *, reading: str = CHORD_READING) -> tuple[dict[tuple[int, int, int], float], dict[tuple[int, int, int], float], dict[tuple[int, int, int], float]]

Compute three maps trijunction (id reg 1, id reg 2, id reg 3) to mean angle, mean angle (deg), length.

Parameters:

Name Type Description Default
unique bool

Count each geometric edge once. Defaults to True.

True
reading str

angle reading, one of foambryo.second_order_angle_reading.ANGLE_READINGS. Keyword-only. Defaults to "chord", which is bit-identical to the historical behaviour. See :func:foambryo.geometry.compute_angles_tri.

CHORD_READING

Returns:

Type Description
tuple[dict[tuple[int, int, int], float], dict[tuple[int, int, int], float], dict[tuple[int, int, int], float]]

tuple[dict, dict, dict]: mean angle (rad), mean angle (deg) and accumulated length.

compute_angles_tri_with_report

compute_angles_tri_with_report(unique: bool = True, *, reading: str = CHORD_READING) -> tuple[dict[tuple[int, int, int], float], dict[tuple[int, int, int], float], dict[tuple[int, int, int], float], AngleReadingReport]

:meth:compute_angles_tri, plus the reading's coverage report.

Parameters:

Name Type Description Default
unique bool

Count each geometric edge once. Defaults to True.

True
reading str

angle reading, one of foambryo.second_order_angle_reading.ANGLE_READINGS. Keyword-only. Defaults to "chord".

CHORD_READING

Returns:

Type Description
tuple[dict[tuple[int, int, int], float], dict[tuple[int, int, int], float], dict[tuple[int, int, int], float], AngleReadingReport]

tuple[dict, dict, dict, AngleReadingReport]: mean angle (rad), mean angle (deg), accumulated length, and which trijunction faces the reading actually covered.

compute_curvatures_interfaces

compute_curvatures_interfaces(weighted: bool = True) -> dict[tuple[int, int], float]

Compute the mean curvature on the interfaces of the mesh.

save

save(filename: str | Path) -> None

Pickle list of vertices, half edges and faces.

load

load(filename: str | Path) -> None

Unpickle list of vertices, half edges and faces (might be dangerous).

separate_faces_dict

separate_faces_dict(triangles_and_labels: NDArray[int64]) -> dict[int, NDArray[np.int64]]

Construct a dictionnary that maps a region id to the array of triangles forming this region.

renormalize_verts

renormalize_verts(points: NDArray[float64], triangles: NDArray[int64]) -> tuple[NDArray[np.float64], NDArray[np.int64]]

Take a mesh made from points and triangles and remove points not indexed in triangles. Re-index triangles.

Return the filtered points and reindexed triangles.

separate_faces_dict_keep_idx

separate_faces_dict_keep_idx(triangles_and_labels: NDArray[int64]) -> tuple[dict[int, NDArray[np.int64]], dict[int, NDArray[np.int64]]]

Construct two dictionnaries to map region -> triangles.

One that maps a region id to the array of triangles forming this region. The other maps a region id to the indices of triangles in the triangles array.

compute_scattered_arrays

compute_scattered_arrays(mesh: DcelData, coeff: float) -> None

Modify the mesh to obtain the alternative "scattered" mesh, with each region separated from others.

coeff = 0 means no displacement.

compute_normal_faces

compute_normal_faces(points: NDArray[float64], triangles: NDArray[int64]) -> NDArray[np.float64]

Compute normals on every triangle.

build_lists

build_lists(points: NDArray[float64], triangles_and_labels: NDArray[int64]) -> tuple[list[Vertex], list[HalfEdge], list[Face]]

Build lists of DCEL objects from mesh.

make_vertices_list

make_vertices_list(points: NDArray[float64]) -> list[Vertex]

Build the list of Vertex from the mesh's points.

mark_trijunctional_vertices

mark_trijunctional_vertices(mesh: DcelData, return_list: bool = False) -> NDArray[np.int64]

Check for vertices on trijunction and mark them as such. Can return the keys of those vertices.

find_trijunctional_edges

find_trijunctional_edges(mesh: DcelData) -> NDArray[np.int64]

Return an array of edges on trijunction.

Each edge is [index of point 1, index of point 2].

compute_length_halfedges

compute_length_halfedges(mesh: DcelData) -> None

Compute all lengths of HalfEdges.

compute_faces_areas

compute_faces_areas(points: NDArray[float64], triangles: NDArray[int64]) -> NDArray[np.float64]

Compute the area of every triangle using Heron's formula.

compute_vertex_normals

compute_vertex_normals(points: NDArray[float64], triangles: NDArray[int64]) -> NDArray[np.float64]

Compute normals at every vertex.

Parameters:

Name Type Description Default
points NDArray[float64]

points of the mesh

required
triangles NDArray[int64]

triangles of the mesh

required

Returns:

Type Description
NDArray[float64]

NDArray[np.float64]: normal for every vertex

remove_unused_vertices

remove_unused_vertices(points: NDArray[float64], triangles_and_labels: NDArray[int64]) -> tuple[NDArray[np.float64], NDArray[np.int64]]

From points and triangles with labels ; remove points not indexed in triangles. Re-index triangles and labels.

Return the filtered points and reindexed triangles and labels.

compute_centroids_graph

compute_centroids_graph(mesh: DcelData) -> NDArray[np.float64]

Return an array of each region's centroid.

compute_networkx_graph

compute_networkx_graph(mesh: DcelData) -> networkx.Graph

Compute a graph with values extracted from the mesh. Nodes = regions, edges = interfaces.

update_graph_with_scattered_values

update_graph_with_scattered_values(graph: Graph, mesh: DcelData) -> networkx.Graph

Move the graph to the mesh's scattered ("exploded") view, from its unexploded positions.

Each cell node, and the vertices of each interface with the medium, move by the displacement of their cell (mesh.clusters_displacements). Call it after mesh.compute_scattered_arrays(coeff). Every node then sits at its exploded cell's barycentre (up to rounding), and an interface with the medium moves with its cell; an interface between two cells does not move (its edge in the graph view is drawn between the two nodes).

The positions are computed from the graph's unexploded ones each time, into new arrays: they are recorded in graph.graph["unexploded_centroid"] and graph.graph["unexploded_verts"] at the first call, so that calling it again on the same graph (a new coefficient) does not add a second displacement, and the arrays the graph was built with (mesh.centroids itself for the nodes) are never modified.

Parameters:

Name Type Description Default
graph Graph

the mesh's graph (compute_networkx_graph), modified and returned.

required
mesh DcelData

the mesh, after compute_scattered_arrays.

required

Returns:

Type Description
Graph

networkx.Graph: graph, its "centroid" node attributes and "verts" edge attributes moved.

compute_verts_and_faces_interfaces

compute_verts_and_faces_interfaces(mesh: DcelData) -> tuple[dict[tuple[int, int], NDArray[np.float64]], dict[tuple[int, int], NDArray[np.int64]]]

Returns two dicts that maps (label1, label2) of an interface to array of vertices & triangles.