def add(self, *items, names=None, classes=None, **kwargs): """ General method to add Actors to the scene. :param items: vedo.Mesh, Actor, (str, Path). If str/path it should be a path to a .obj or .stl file. Whatever the input it's turned into an instance of Actor before adding it to the scne :param names: names to be assigned to the Actors :param classs: br_classes to be assigned to the Actors :param **kwargs: parameters to be passed to the individual loading functions (e.g. to load from file and specify the color) """ names = names or [None for a in items] classes = classes or [None for a in items] # turn items into Actors actors = [] for item, name, _class in zip(items, listify(names), listify(classes)): if item is None: continue if isinstance(item, (Mesh, Assembly)): actors.append(Actor(item, name=name, br_class=_class)) elif pi.utils._class_name(item) == "vtkCornerAnnotation": # Mark text actors differently because they don't behave like # other 3d actors actors.append( Actor( item, name=name, br_class=_class, is_text=True, **kwargs, )) elif pi.utils._class_name(item) == "Volume" and not isinstance( item, Volume): actors.append( Volume(item, name=name, br_class=_class, **kwargs)) elif isinstance(item, Actor): actors.append(item) elif isinstance(item, (str, Path)): mesh = load_mesh_from_file(item, **kwargs) name = name or Path(item).name _class = _class or "from file" actors.append(Actor(mesh, name=name, br_class=_class)) else: raise ValueError( f"Unrecognized argument: {item} [{pi.utils._class_name(item)}]" ) # Add to the lists actors self.actors.extend(actors) return return_list_smart(actors)
def get_actors(self, name=None, br_class=None): """ Return's the scene's actors that match some search criteria. :param name: strm int or list of str/int, actors' names :param br_class: str or list of str, actors br classes """ matches = self.actors if name is not None: name = listify(name) matches = [m for m in matches if m.name in name] if br_class is not None: br_class = listify(br_class) matches = [m for m in matches if m.br_class in br_class] return matches
def slice( self, plane: [str, Plane], actors=None, close_actors=False, ): """ Slices actors with a plane. :param plane: str, Plane. If a string it needs to be a supported plane from brainglobe's atlas api (e.g. 'frontal') otherwise it should be a vedo.Plane mesh :param actors: list of actors to be sliced. If None all actors will be sliced :param close_actors: If true the openings in the actors meshes caused by teh cut will be closed. """ if isinstance(plane, str): plane = self.atlas.get_plane(plane=plane) if not actors or actors is None: actors = self.clean_actors.copy() for actor in listify(actors): actor._mesh = actor._mesh.cutWithPlane( origin=plane.center, normal=plane.normal, ) if close_actors: actor.cap() if actor.silhouette is not None: self.plotter.remove(actor.silhouette.mesh) self.plotter.add(actor.make_silhouette().mesh)
def experiments_source_search(SOI): """ Returns data about experiments whose injection was in the SOI, structure of interest :param SOI: str, structure of interest. Acronym of structure to use as seed for teh search :param source: (Default value = True) """ transgenic_id = 0 # id = 0 means use only wild type primary_structure_only = True if not allen_sdk_installed: print( f"[{orange}]Allen skd package is not installed, cannot download streamlines data." "Please install `allensdk` with `pip install allensdk` (note: this requires python < 3.8)" ) return None return pd.DataFrame( mca.experiment_source_search( injection_structures=listify(SOI), target_domain=None, transgenic_lines=transgenic_id, primary_structure_only=primary_structure_only, ) )
def add_brain_region(self, *regions, alpha=1, color=None, silhouette=True, hemisphere="both"): """ Dedicated method to add brain regions to render :param regions: str. String of regions names :param alpha: float :param color: str. If None the atlas default color is used :param silhouette: bool. If true regions Actors will have a silhouette :param hemisphere: str. - if "both" the complete mesh is returned - if "left"/"right" only the corresponding half of the mesh is returned """ # get regions actors from atlas regions = self.atlas.get_region(*regions, alpha=alpha, color=color) regions = listify(regions) or [] if hemisphere == "right": plane = self.atlas.get_plane(plane="sagittal", norm=(0, 0, -1)) elif hemisphere == "left": plane = self.atlas.get_plane(plane="sagittal", norm=(0, 0, 1)) if hemisphere in ("left", "right"): self.slice(plane, actors=regions, close_actors=True) if silhouette and regions: self.add_silhouette(*regions) return self.add(*regions)
def experiments_source_search(SOI): """ Returns data about experiments whose injection was in the SOI, structure of interest :param SOI: str, structure of interest. Acronym of structure to use as seed for teh search :param source: (Default value = True) """ transgenic_id = 0 # id = 0 means use only wild type primary_structure_only = True return pd.DataFrame( mca.experiment_source_search( injection_structures=listify(SOI), target_domain=None, transgenic_lines=transgenic_id, primary_structure_only=primary_structure_only, ) )
def slice( self, plane: [str, Plane], actors=None, close_actors=False, ): """ Slices actors with a plane. :param plane: str, Plane. If a string it needs to be a supported plane from brainglobe's atlas api (e.g. 'frontal') otherwise it should be a vedo.Plane mesh :param actors: list of actors to be sliced. If None all actors will be sliced :param close_actors: If true the openings in the actors meshes caused by teh cut will be closed. """ if self.transform_applied: print( f"[b {salmon}]Warning: [/b {salmon}][{amber}]you're attempting to cut actors with a plane " + "after having rendered the scene, this might give unpredictable results." + "\nIt's advised to perform all cuts before the first call to `render`" ) if isinstance(plane, str): plane = self.atlas.get_plane(plane=plane) actors = actors or self.clean_actors.copy() for actor in listify(actors): actor.mesh = actor.mesh.cutWithPlane( origin=plane.center, normal=plane.normal, ) if close_actors: actor.cap()
def add_brain_region( self, *regions, alpha=1, color=None, silhouette=None, hemisphere="both", force=False, ): """ Dedicated method to add brain regions to render :param regions: str. String of regions names :param alpha: float :param color: str. If None the atlas default color is used :param silhouette: bool. If true regions Actors will have a silhouette :param hemisphere: str. - if "both" the complete mesh is returned - if "left"/"right" only the corresponding half of the mesh is returned :param force: force adding of region even if already rendred """ if silhouette is None: silhouette = ( silhouette or True if settings.SHADER_STYLE == "cartoon" else False ) # avoid adding regions already rendered if not force: already_in = [ r.name for r in self.get_actors(br_class="brain region") ] regions = [r for r in regions if r not in already_in] if not regions: # they were all already rendered logger.debug( "Not adding any region because they are all already in the scene" ) return None logger.debug( f"SCENE: Adding {len(regions)} brain regions to scene: {regions}" ) # get regions actors from atlas regions = self.atlas.get_region(*regions, alpha=alpha, color=color) regions = listify(regions) or [] # add actors if silhouette and regions and alpha: self.add_silhouette(*regions) actors = self.add(*regions) # slice if hemisphere == "right": plane = self.atlas.get_plane( pos=self.root._mesh.centerOfMass(), norm=(0, 0, 1) ) elif hemisphere == "left": plane = self.atlas.get_plane( pos=self.root._mesh.centerOfMass(), norm=(0, 0, -1) ) if hemisphere in ("left", "right"): self.slice(plane, actors=actors, close_actors=True) return actors
def make_actor_label( atlas, actors, labels, size=300, color=None, radius=100, xoffset=0, yoffset=-500, zoffset=0, ): """ Adds a 2D text ancored to a point on the actor's mesh to label what the actor is :param kwargs: key word arguments can be passed to determine text appearance and location: - size: int, text size. Default 300 - color: str, text color. A list of colors can be passed if None the actor's color is used. Default None. - xoffset, yoffset, zoffset: integers that shift the label position - radius: radius of sphere used to denote label anchor. Set to 0 or None to hide. """ offset = [-yoffset, -zoffset, xoffset] default_offset = np.array([0, -200, 100]) new_actors = [] for n, (actor, label) in enumerate(zip(listify(actors), listify(labels))): # Get label color if color is None: color = actor.c() # Get mesh's highest point points = actor.points().copy() point = points[np.argmin(points[:, 1]), :] point += np.array(offset) + default_offset try: if atlas.hemisphere_from_coords(point, as_string=True) == "left": point = atlas.mirror_point_across_hemispheres(point) except IndexError: pass # Create label txt = Text(label, point, s=size, c=color) txt._kwargs = dict( size=size, color=color, radius=radius, xoffset=xoffset, yoffset=yoffset, zoffset=zoffset, ) new_actors.append(txt) # Mark a point on Mesh that corresponds to the label location if radius is not None: pt = actor.closestPoint(point) sphere = Sphere(pt, r=radius, c=color) sphere.ancor = pt new_actors.append(sphere) return new_actors
def test_listify(): assert isinstance(listify([1, 2, 3]), list) assert isinstance(listify((1, 2, 3)), list) assert isinstance(listify(1), list)