Source code for brainrender.atlas

"""Atlas subclass adding region and plane Actor support for scenes."""

from typing import Any

import numpy as np
import numpy.typing as npt
from brainglobe_atlasapi.bg_atlas import BrainGlobeAtlas
from loguru import logger
from vedo import Plane

from brainrender import settings
from brainrender._io import convert_meshio_to_vedo
from brainrender._utils import return_list_smart
from brainrender.actor import Actor


[docs] class Atlas(BrainGlobeAtlas): """ Subclass of BrainGlobeAtlas with helpers for rendering. Parameters ---------- atlas_name Falls back to ``settings.DEFAULT_ATLAS`` if None. check_latest Check for the latest atlas version. Default True. """ def __init__( self, atlas_name: str | None = None, check_latest: bool = True, ) -> None: atlas_name = atlas_name or settings.DEFAULT_ATLAS self.atlas_name = atlas_name logger.debug(f"Generating ATLAS: {atlas_name}") try: super().__init__(atlas_name=atlas_name, print_authors=False) except TypeError: # The latest version of BGatlas has no print_authors argument super().__init__(atlas_name=atlas_name, check_latest=check_latest) @property def zoom(self) -> float: """ Return a reasonable camera zoom given the atlas resolution. """ res = np.max(self.metadata["resolution"]) if self.atlas_name == "allen_human_500um": logger.debug( "ATLAS: setting zoom manually for human atlas, atlas needs fixing" ) return 350 else: return 40 / res def _get_region_color(self, region: str | int) -> list[float]: """ Get the rgb color of a region in the atlas. Parameters ---------- region Region acronym or ID. Returns ------- list of float """ return [ x / 255 for x in self._get_from_structure(region, "rgb_triplet") ]
[docs] def get_region( self, *regions: str | int, alpha: float = 1, color: str | list[float] | None = None, ) -> Actor | list[Actor] | None: """ Get brain regions meshes as Actors. Parameters ---------- *regions Region acronyms or IDs. alpha Mesh transparency. Default 1. color Uses atlas RGB colour if None. Returns ------- Actor or list of Actor or None """ if not regions: return None _color = color actors = [] for region in regions: if ( region not in self.lookup_df.acronym.values and region not in self.lookup_df["id"].values ): print( f"The region {region} doesn't seem to belong to the atlas being used: {self.atlas_name}. Skipping" ) continue # Get mesh try: meshio_mesh = self.mesh_from_structure(region) mesh = convert_meshio_to_vedo( meshio_mesh, color=color, alpha=alpha ) except FileNotFoundError: print( f"The region {region} is in the ontology but does not have a corresponding volume in the atlas being used: {self.atlas_name}. Skipping" ) continue # Get color color = color or self._get_region_color(region) # Make actor actor = Actor(mesh, name=region, br_class="brain region") actor.c(color).alpha(alpha) actors.append(actor) # reset color to input color = _color return return_list_smart(actors)
[docs] def get_plane( self, pos: npt.ArrayLike | None = None, norm: npt.ArrayLike | None = None, plane: str | None = None, sx: float | None = None, sy: float | None = None, color: str = "lightgray", alpha: float = 0.25, **kwargs: Any, ) -> Actor: """ Returns a plane going through a point at pos, oriented orthogonally to the ``norm`` vector and of width and height sx, sy. Parameters ---------- pos (x, y, z) the plane passes through. Defaults to root centre of mass. norm Normal vector. Derived from *plane* if not given. plane ``"sagittal"``, ``"horizontal"``, or ``"frontal"``. sx Width. Inferred from root bounds if None. sy Height. Inferred from root bounds if None. color Default ``"lightgray"``. alpha Default 0.25. Returns ------- Actor Raises ------ ValueError If *plane* has no matching normal in the atlas space. """ axes_pairs = dict(sagittal=(0, 1), horizontal=(2, 0), frontal=(2, 1)) if pos is None: pos = self.root._mesh.center_of_mass() try: norm = norm or self.space.plane_normals[plane] except KeyError: # pragma: no cover raise ValueError( # pragma: no cover f"Could not find normals for plane {plane}. Atlas space provides these normals: {self.space.plane_normals}" # pragma: no cover ) # Get plane width and height idx_pair = ( axes_pairs[plane] if plane is not None else axes_pairs["horizontal"] ) bounds = self.root.bounds() root_bounds = [ [bounds[0], bounds[1]], [bounds[2], bounds[3]], [bounds[4], bounds[5]], ] wh = [float(np.diff(root_bounds[i])[0]) for i in idx_pair] if sx is None: sx = wh[0] if sy is None: sy = wh[1] # return plane return Actor( Plane(pos=pos, normal=norm, s=(sx, sy), c=color, alpha=alpha), name=f"Plane at {pos} norm: {norm}", br_class="plane", )