"""Factory functions for volumetric and voxel-based objects."""
import numpy as np
from typing import Any
from typing import Dict as TypingDict
from typing import List as TypingList
from typing import Optional, Tuple, Union
from .common import _default_color, default_colormap, nice_colors
from ..helpers import check_attribute_color_range
from ..objects import (MIP, MarchingCubes, SparseVoxels, Volume, VolumeSlice,
VoxelChunk, Voxels, VoxelsGroup)
from ..transform import process_transform_arguments
# Type aliases for better readability
ArrayLike = Union[TypingList, np.ndarray, Tuple]
ColorMap = Union[TypingList[TypingList[float]], TypingDict[str, Any], np.ndarray]
ColorRange = TypingList[float]
OpacityFunction = TypingList[float]
[docs]
def volume(
volume: ArrayLike,
color_map: Optional[ColorMap] = None,
opacity_function: Optional[OpacityFunction] = None,
color_range: ColorRange = None,
samples: float = 512.0,
alpha_coef: float = 50.0,
gradient_step: float = 0.005,
shadow: str = "off",
interpolation: bool = True,
shadow_delay: int = 500,
shadow_res: int = 128,
focal_length: float = 0.0,
focal_plane: float = 100.0,
ray_samples_count: int = 16,
mask: ArrayLike = None,
mask_opacities: ArrayLike = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> Volume:
if color_range is None:
color_range = []
if mask is None:
mask = []
if mask_opacities is None:
mask_opacities = []
if color_map is None:
color_map = default_colormap
color_range = (
check_attribute_color_range(volume, color_range)
if type(color_range) is not dict
else color_range
)
if opacity_function is None:
opacity_function = [np.min(color_map[::4]), 0.0, np.max(color_map[::4]), 1.0]
return process_transform_arguments(
Volume(
volume=volume,
color_map=color_map,
opacity_function=opacity_function,
color_range=color_range,
compression_level=compression_level,
samples=samples,
alpha_coef=alpha_coef,
gradient_step=gradient_step,
interpolation=interpolation,
shadow=shadow,
shadow_delay=shadow_delay,
shadow_res=shadow_res,
focal_plane=focal_plane,
focal_length=focal_length,
mask=mask,
mask_opacities=mask_opacities,
name=name,
group=group,
custom_data=custom_data,
ray_samples_count=ray_samples_count,
),
**kwargs,
)
[docs]
def mip(
volume: ArrayLike,
color_map: Optional[ColorMap] = None,
opacity_function: Optional[OpacityFunction] = None,
color_range: ColorRange = None,
samples: float = 512.0,
gradient_step: float = 0.005,
interpolation: bool = True,
mask: ArrayLike = None,
mask_opacities: ArrayLike = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> MIP:
if color_range is None:
color_range = []
if mask is None:
mask = []
if mask_opacities is None:
mask_opacities = []
if color_map is None:
color_map = default_colormap
color_range = (
check_attribute_color_range(volume, color_range)
if type(color_range) is not dict
else color_range
)
if opacity_function is None:
opacity_function = [np.min(color_map[::4]), 0.0, np.max(color_map[::4]), 1.0]
return process_transform_arguments(
MIP(
volume=volume,
color_map=color_map,
opacity_function=opacity_function,
color_range=color_range,
samples=samples,
gradient_step=gradient_step,
interpolation=interpolation,
mask=mask,
mask_opacities=mask_opacities,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
),
**kwargs,
)
[docs]
def volume_slice(
volume: ArrayLike = None,
color_map: Optional[ColorMap] = None,
color_range: ColorRange = None,
opacity_function: OpacityFunction = None,
opacity: float = 1.0,
mask: ArrayLike = None,
active_masks: ArrayLike = None,
color_map_masks: Optional[ColorMap] = None,
mask_opacity: float = 0.5,
slice_x: int = -1,
slice_y: int = -1,
slice_z: int = 0,
interpolation: int = 1,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> VolumeSlice:
if volume is None:
volume = []
if color_range is None:
color_range = []
if opacity_function is None:
opacity_function = []
if mask is None:
mask = []
if active_masks is None:
active_masks = []
if color_map is None:
color_map = default_colormap
if color_map_masks is None:
color_map_masks = nice_colors
color_map = (
np.array(color_map, np.float32) if type(color_map) is not dict else color_map
)
if len(volume) > 0:
color_range = check_attribute_color_range(volume, color_range)
return process_transform_arguments(
VolumeSlice(
volume=volume,
color_map=color_map,
color_range=color_range,
opacity_function=opacity_function,
opacity=opacity,
slice_x=slice_x,
slice_y=slice_y,
slice_z=slice_z,
interpolation=interpolation,
mask=mask,
mask_opacity=mask_opacity,
active_masks=active_masks,
color_map_masks=color_map_masks,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
),
**kwargs,
)
[docs]
def voxels(
voxels: ArrayLike,
color_map: Optional[ColorMap] = None,
wireframe: bool = False,
outlines: bool = True,
outlines_color: int = 0,
opacity: float = 1.0,
bounds: Optional[ArrayLike] = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> Voxels:
if color_map is None:
color_map = nice_colors
if bounds is not None:
kwargs["bounds"] = bounds
else:
max_z, max_y, max_x = np.shape(voxels)
kwargs["bounds"] = np.array([0, max_x, 0, max_y, 0, max_z])
return process_transform_arguments(
Voxels(
voxels=voxels,
color_map=color_map,
wireframe=wireframe,
outlines=outlines,
outlines_color=outlines_color,
opacity=opacity,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
),
**kwargs,
)
[docs]
def sparse_voxels(
sparse_voxels: ArrayLike,
space_size: ArrayLike,
color_map: Optional[ColorMap] = None,
wireframe: bool = False,
outlines: bool = True,
outlines_color: int = 0,
opacity: float = 1.0,
bounds: Optional[ArrayLike] = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> SparseVoxels:
if color_map is None:
color_map = nice_colors
assert (
isinstance(space_size, (tuple, list, np.ndarray))
and np.shape(space_size) == (3,)
and all(d > 0 for d in space_size)
)
return process_transform_arguments(
SparseVoxels(
sparse_voxels=sparse_voxels,
space_size=space_size,
color_map=color_map,
wireframe=wireframe,
outlines=outlines,
outlines_color=outlines_color,
opacity=opacity,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
),
**kwargs,
)
[docs]
def voxels_group(
space_size: ArrayLike,
voxels_group: TypingList[TypingDict[str, Any]] = None,
chunks_ids: TypingList[int] = None,
color_map: Optional[ColorMap] = None,
wireframe: bool = False,
outlines: bool = True,
outlines_color: int = 0,
opacity: float = 1.0,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> VoxelsGroup:
if voxels_group is None:
voxels_group = []
if chunks_ids is None:
chunks_ids = []
if color_map is None:
color_map = nice_colors
for g in voxels_group:
g["coord"] = np.array(g["coord"])
g["voxels"] = np.array(g["voxels"])
if "multiple" not in g:
g["multiple"] = 1
return process_transform_arguments(
VoxelsGroup(
voxels_group=voxels_group,
chunks_ids=chunks_ids,
space_size=space_size,
color_map=color_map,
wireframe=wireframe,
outlines=outlines,
outlines_color=outlines_color,
opacity=opacity,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
),
**kwargs,
)
[docs]
def marching_cubes(
scalar_field: ArrayLike,
level: float,
color: int = _default_color,
attribute: ArrayLike = None,
color_map: Optional[ColorMap] = None,
color_range: ColorRange = None,
opacity_function: OpacityFunction = None,
wireframe: bool = False,
flat_shading: bool = True,
shininess: float = 50.0,
opacity: float = 1.0,
spacings_x: ArrayLike = None,
spacings_y: ArrayLike = None,
spacings_z: ArrayLike = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
**kwargs: Any,
) -> MarchingCubes:
if attribute is None:
attribute = []
if color_range is None:
color_range = []
if opacity_function is None:
opacity_function = []
if spacings_x is None:
spacings_x = []
if spacings_y is None:
spacings_y = []
if spacings_z is None:
spacings_z = []
if color_map is None:
color_map = default_colormap
attribute = (
np.array(attribute, np.float32) if type(attribute) is not dict else attribute
)
color_range = check_attribute_color_range(attribute, color_range)
return process_transform_arguments(
MarchingCubes(
scalar_field=scalar_field,
spacings_x=spacings_x,
spacings_y=spacings_y,
spacings_z=spacings_z,
color=color,
attribute=attribute,
color_map=color_map,
color_range=color_range,
opacity_function=opacity_function,
level=level,
wireframe=wireframe,
flat_shading=flat_shading,
shininess=shininess,
opacity=opacity,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
),
**kwargs,
)
[docs]
def voxel_chunk(
voxels: ArrayLike, coord: ArrayLike, multiple: int = 1, compression_level: int = 0
) -> VoxelChunk:
"""Create a VoxelChunk object for selective updating voxels.
Parameters
----------
voxels : array_like
Array of voxel data.
coord : array_like
Coordinates for the chunk.
multiple : int, optional
Multiple factor, by default 1.
compression_level : int, optional
Compression level for the chunk, by default 0.
Returns
-------
VoxelChunk
VoxelChunk object.
"""
return VoxelChunk(
voxels=np.array(voxels, np.uint8),
coord=np.array(coord, np.uint32),
multiple=multiple,
compression_level=compression_level,
)