[docs]classLayout:"""Provide functionalities for laying out actors in a 3D scene."""
[docs]defapply(self,actors):"""Position the actors according to a certain layout."""positions=self.compute_positions(actors)fora,posinzip(actors,positions):ifis_ui(a):a.position=(pos[0],pos[1])else:anchor=np.array(getattr(a,"anchor",(0,0,0)))a.AddPosition(pos-(np.array(a.GetCenter())+anchor))
[docs]defcompute_positions(self,_actors):"""Compute the 3D coordinates of some actors."""return[]
[docs]classGridLayout(Layout):"""Provide functionalities for laying out actors in a 2D grid fashion. The `GridLayout` class lays the actors in a 2D structured grid aligned with the xy-plane. """@warn_on_args_to_kwargs()def__init__(self,*,cell_padding=0,cell_shape="rect",aspect_ratio=16/9.0,dim=None,position_offset=(0,0,0),):"""Initialize the grid layout. Parameters ---------- cell_padding : 2-tuple of float or float (optional) Each grid cell will be padded according to (pad_x, pad_y) i.e. horizontally and vertically. Padding is evenly distributed on each side of the cell. If a single float is provided then both pad_x and pad_y will have the same value. cell_shape : {'rect', 'square', 'diagonal'} (optional) Specifies the desired shape of every grid cell. 'rect' ensures the cells are the tightest. 'square' ensures the cells are as wide as high. 'diagonal' ensures the content of the cells can be rotated without colliding with content of the neighboring cells. aspect_ratio : float (optional) Aspect ratio of the grid (width/height). Default: 16:9. dim : tuple of int (optional) Dimension (nb_rows, nb_cols) of the grid. If provided, `aspect_ratio` will be ignored. position_offset: tuple (optional) Offset the grid by some factor """self.cell_shape=cell_shapeself.aspect_ratio=aspect_ratioself.dim=dimself.position_offset=position_offsetifisinstance(cell_padding,int):self.cell_padding=(cell_padding,cell_padding)else:self.cell_padding=cell_padding
[docs]defget_cells_shape(self,actors):"""Get the 2D shape (on the xy-plane) of some actors according to `self.cell_shape`. Parameters ---------- actors : list of `vtkProp3D` objects Actors from which to calculate the 2D shape. Returns ------- list of 2-tuple The 2D shape (on the xy-plane) of every actors. """ifself.cell_shape=="rect":bounding_box_sizes=np.asarray(list(map(self.compute_sizes,actors,)))cell_shape=np.max(bounding_box_sizes,axis=0)[:2]shapes=[cell_shape]*len(actors)elifself.cell_shape=="square":bounding_box_sizes=np.asarray(list(map(self.compute_sizes,actors,)))cell_shape=np.max(bounding_box_sizes,axis=0)[:2]shapes=[(max(cell_shape),)*2]*len(actors)elifself.cell_shape=="diagonal":# Size of every cell corresponds to the diagonal# of the largest bounding box.diagonals=[]forainactors:ifis_ui(a):width,height=a.sizediagonal=math.sqrt(width**2+height**2)diagonals.append(diagonal)else:diagonals.append(a.GetLength())longest_diagonal=np.max(diagonals)shapes=[(longest_diagonal,longest_diagonal)]*len(actors)else:raiseValueError("Unknown cell shape: '{0}'".format(self.cell_shape,))returnshapes
[docs]defcompute_positions(self,actors):"""Compute the 3D coordinates of some actors. The coordinates will lie on the xy-plane and form a 2D grid. Parameters ---------- actors : list of `vtkProp3D` objects Actors to be layout in a grid manner. Returns ------- list of 3-tuple The computed 3D coordinates of every actors. """shapes=self.get_cells_shape(actors)# Add padding, if any, around every cell.shapes=[np.array(self.cell_padding)/2.0+sforsinshapes]positions=get_grid_cells_position(shapes,aspect_ratio=self.aspect_ratio,dim=self.dim,)positions+=self.position_offsetreturnpositions
[docs]defcompute_sizes(self,actor):"""Compute the bounding box size of the actor/UI element Parameters ---------- actor: `vtkProp3D` or `UI` element Actor/UI element whose size is to be calculated Returns ------- bounding box sizes: tuple """ifis_ui(actor):width,height=actor.sizereturn(width,height,0)returnget_bounding_box_sizes(actor)
[docs]classHorizontalLayout(GridLayout):"""Provide functionalities for laying out actors in a horizontal layout."""@warn_on_args_to_kwargs()def__init__(self,*,cell_padding=0,cell_shape="rect"):"""Initialize the Horizontal layout. Parameters ---------- cell_padding : 2-tuple of float or float (optional) Each grid cell will be padded according to (pad_x, pad_y) i.e. horizontally and vertically. Padding is evenly distributed on each side of the cell. If a single float is provided then both pad_x and pad_y will have the same value. cell_shape : {'rect', 'square', 'diagonal'} (optional) Specifies the desired shape of every grid cell. 'rect' ensures the cells are the tightest. 'square' ensures the cells are as wide as high. 'diagonal' ensures the content of the cells can be rotated without colliding with content of the neighboring cells. """super(HorizontalLayout,self).__init__(cell_padding=cell_padding,cell_shape=cell_shape)
[docs]defcompute_positions(self,actors):"""Compute the 3D coordinates of some actors. The coordinates will lie on the xy-plane and form a horizontal stack. Parameters ---------- actors : list of `vtkProp3D` objects Actors to be layout in a horizontal fashion. Returns ------- list of 3-tuple The computed 3D coordinates of every actors. """positions=[np.asarray([0,0,0]),]shapes=self.get_cells_shape(actors[1:])# Add padding, if any, around every cell.shapes=[np.array(self.cell_padding)/2.0+sforsinshapes]forshapeinshapes:actor_position=positions[-1]+np.asarray([shape[0],0,0])positions.append(actor_position)returnpositions
[docs]classVerticalLayout(GridLayout):"""Provide functionalities for laying out actors in a vertical stack."""@warn_on_args_to_kwargs()def__init__(self,*,cell_padding=0,cell_shape="rect"):"""Initialize the Vertical layout. Parameters ---------- cell_padding : 2-tuple of float or float (optional) Each cell will be padded according to (pad_x, pad_y) i.e. horizontally and vertically. Padding is evenly distributed on each side of the cell. If a single float is provided then both pad_x and pad_y will have the same value. cell_shape : {'rect', 'square', 'diagonal'} (optional) Specifies the desired shape of every cell. 'rect' ensures the cells are the tightest. 'square' ensures the cells are as wide as high. 'diagonal' ensures the content of the cells can be rotated without colliding with content of the neighboring cells. """super(VerticalLayout,self).__init__(cell_padding=cell_padding,cell_shape=cell_shape)
[docs]defcompute_positions(self,actors):"""Compute the 3D coordinates of some actors. Parameters ---------- actors : list of `vtkProp3D` objects Actors to be layout in a vertical stack. Returns ------- list of 3-tuple The computed 3D coordinates of every actors. """positions=[np.asarray([0,0,0]),]shapes=self.get_cells_shape(actors[1:])# Add padding, if any, around every cell.shapes=[np.array(self.cell_padding)/2.0+sforsinshapes]forshapeinshapes:actor_position=positions[-1]+np.asarray([0,shape[1],0])positions.append(actor_position)returnpositions
[docs]classXLayout(HorizontalLayout):"""Provide functionalities for laying out actors along x-axis."""@warn_on_args_to_kwargs()def__init__(self,*,direction="x+",cell_padding=0,cell_shape="rect"):"""Initialize the X layout. Parameters ---------- direction: str, optional The direction of layout. 'x+' means actors will be placed along positive x-axis. 'x-' means actors will be placed along negative x-axis. cell_padding : 2-tuple of float or float (optional) Each cell will be padded according to (pad_x, pad_y) i.e. horizontally and vertically. Padding is evenly distributed on each side of the cell. If a single float is provided then both pad_x and pad_y will have the same value. cell_shape : {'rect', 'square', 'diagonal'} (optional) Specifies the desired shape of every cell. 'rect' ensures the cells are the tightest. 'square' ensures the cells are as wide as high. 'diagonal' ensures the content of the cells can be rotated without colliding with content of the neighboring cells. """self.direction=direction.lower()ifself.directionnotin["x+","x-"]:raiseValueError(f"{direction} is not a valid direction")super(XLayout,self).__init__(cell_padding=cell_padding,cell_shape=cell_shape,)
[docs]defget_cells_shape(self,actors):"""Get the 2D shape (on the xy-plane) of some actors according to `self.cell_shape`. Parameters ---------- actors : list of `vtkProp3D` objects Actors from which to calculate the 2D shape. Returns ------- list of 2-tuple The 2D shape (on the xy-plane) of every actors. """ifself.direction=="x-":actors=actors[::-1]returnsuper().get_cells_shape(actors)
[docs]defcompute_positions(self,actors):"""Compute the 3D coordinates of some actors. The coordinates will lie on the xy-plane and will be placed along x-axis. Parameters ---------- actors : list of `vtkProp3D` objects Actors to be layout along the x-axis. Returns ------- list of 3-tuple The computed 3D coordinates of every actors. """ifself.direction=="x-":actors=actors[::-1]returnsuper().compute_positions(actors)
[docs]defapply(self,actors):"""Position the actors according to a certain layout."""ifself.direction=="x-":actors=actors[::-1]returnsuper().apply(actors)
[docs]classYLayout(VerticalLayout):"""Provide functionalities for laying out actors along y-axis."""@warn_on_args_to_kwargs()def__init__(self,*,direction="y+",cell_padding=0,cell_shape="rect"):"""Initialize the Y layout. Parameters ---------- direction: str, optional The direction of layout. 'y+' means actors will be placed along positive y-axis. 'y-' means actors will be placed along negative y-axis. cell_padding : 2-tuple of float or float (optional) Each cell will be padded according to (pad_x, pad_y) i.e. horizontally and vertically. Padding is evenly distributed on each side of the cell. If a single float is provided then both pad_x and pad_y will have the same value. cell_shape : {'rect', 'square', 'diagonal'} (optional) Specifies the desired shape of every cell. 'rect' ensures the cells are the tightest. 'square' ensures the cells are as wide as high. 'diagonal' ensures the content of the cells can be rotated without colliding with content of the neighboring cells. """self.direction=direction.lower()ifself.directionnotin["y+","y-"]:raiseValueError(f"{direction} is not a valid direction")super(YLayout,self).__init__(cell_padding=cell_padding,cell_shape=cell_shape,)
[docs]defget_cells_shape(self,actors):"""Get the 2D shape (on the xy-plane) of some actors according to `self.cell_shape`. Parameters ---------- actors : list of `vtkProp3D` objects Actors from which to calculate the 2D shape. Returns ------- list of 2-tuple The 2D shape (on the xy-plane) of every actors. """ifself.direction=="y-":actors=actors[::-1]returnsuper().get_cells_shape(actors)
[docs]defcompute_positions(self,actors):"""Compute the 3D coordinates of some actors. The coordinates will lie on the xy-plane and will be placed along y-axis. Parameters ---------- actors : list of `vtkProp3D` objects Actors to be layout along the y-axis. Returns ------- list of 3-tuple The computed 3D coordinates of every actors. """ifself.direction=="y-":actors=actors[::-1]returnsuper().compute_positions(actors)
[docs]defapply(self,actors):"""Position the actors according to a certain layout."""ifself.direction=="y-":actors=actors[::-1]returnsuper().apply(actors)
[docs]classZLayout(GridLayout):"""Provide functionalities for laying out actors along z-axis."""@warn_on_args_to_kwargs()def__init__(self,*,direction="z+",cell_padding=0,cell_shape="rect"):"""Initialize the Z layout. Parameters ---------- direction: str, optional The direction of layout. 'z+' means actors will be placed along positive z-axis. 'z-' means actors will be placed along negative z-axis. cell_padding : 2-tuple of float or float (optional) Each cell will be padded according to (pad_x, pad_y) i.e. horizontally and vertically. Padding is evenly distributed on each side of the cell. If a single float is provided then both pad_x and pad_y will have the same value. cell_shape : {'rect', 'square', 'diagonal'} (optional) Specifies the desired shape of every cell. 'rect' ensures the cells are the tightest. 'square' ensures the cells are as wide as high. 'diagonal' ensures the content of the cells can be rotated without colliding with content of the neighboring cells. """self.direction=direction.lower()ifself.directionnotin["z+","z-"]:raiseValueError(f"{direction} is not a valid direction")super(ZLayout,self).__init__(cell_padding=cell_padding,cell_shape=cell_shape,)
[docs]defget_cells_shape(self,actors):"""Get the shape (on the z-plane) of some actors according to `self.cell_shape`. Parameters ---------- actors : list of `vtkProp3D` objects Actors from which to calculate the shape. Returns ------- list of floats The shape (on the z-plane) of every actors. """ifself.direction=="z-":actors=actors[::-1]ifself.cell_shape=="rect"orself.cell_shape=="square":bounding_box_sizes=np.asarray(list(map(get_bounding_box_sizes,actors,)))cell_shape=np.max(bounding_box_sizes,axis=0)[2]shapes=[cell_shape]*len(actors)elifself.cell_shape=="diagonal":# Size of every cell corresponds to the diagonal# of the largest bounding box.longest_diagonal=np.max([a.GetLength()forainactors])shapes=[longest_diagonal]*len(actors)else:raiseValueError("Unknown cell shape: '{0}'".format(self.cell_shape,))returnshapes
[docs]defcompute_positions(self,actors):"""Compute the 3D coordinates of some actors. Parameters ---------- actors : list of `vtkProp3D` objects Actors to be layout along the z-axis. Returns ------- list of 3-tuple The computed 3D coordinates of every actors. """ifself.direction=="z-":actors=actors[::-1]positions=[np.asarray([0,0,0]),]shapes=self.get_cells_shape(actors[1:])# Add padding, if any, around every cell.shapes=[np.array(self.cell_padding)/2.0+sforsinshapes]forshapeinshapes:actor_position=positions[-1]+np.asarray([0,0,shape[0]])positions.append(actor_position)returnpositions
[docs]defapply(self,actors):"""Position the actors according to a certain layout."""ifself.direction=="z-":actors=actors[::-1]returnsuper().apply(actors)