"""Factory functions for geometric objects."""
import warnings
from typing import Any, Callable, Optional, Tuple, Union
from typing import Dict as TypingDict
from typing import List as TypingList
import numpy as np
from ..helpers import check_attribute_color_range, image_format, pack_colors
from ..objects import STL, Line, Lines, Mesh, Surface
from ..objects.base import has_data
from ..transform import process_transform_arguments
from .common import _default_color, default_colormap, factory_color
# only LineMesh builds a lit material; thick and simple draw unlit tubes and lines, so these
# two traits reach the browser and nothing reads them
LINE_MATERIAL_DEFAULTS = {"roughness": 0.4, "metalness": 0.0}
def _warn_unlit_line(shader, roughness, metalness):
if shader == "mesh":
return
ignored = [name for name, value in (("roughness", roughness), ("metalness", metalness))
if value is not None and value != LINE_MATERIAL_DEFAULTS[name]]
if ignored:
warnings.warn(
"%s %s ignored by the '%s' line shader, which draws unlit geometry - use "
"shader='mesh' for a lit line" % (
" and ".join(ignored), "are" if len(ignored) > 1 else "is", shader),
stacklevel=3,
)
# Type aliases for better readability
ArrayLike = Union[TypingList, np.ndarray, Tuple]
ColorMap = Union[TypingList[TypingList[float]], TypingDict[str, Any], np.ndarray]
ColorRange = TypingList[float]
[docs]
def lines(
vertices: ArrayLike,
indices: ArrayLike,
indices_type: str = "triangle",
color: int = _default_color,
colors: TypingList[int] = None, # lgtm [py/similar-function]
attribute: ArrayLike = None,
color_map: Optional[ColorMap] = None,
color_range: ColorRange = None,
width: float = 0.01,
shader: str = "thick",
roughness: float = 0.4,
metalness: float = 0.0,
shininess: float = None,
radial_segments: int = 8,
opacity: float = 1.0,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
visible: bool = True,
**kwargs: Any,
) -> Lines:
"""
Create a Line drawable for plotting segments and polylines.
Parameters
----------
vertices : array_like
Array with (x, y, z) coordinates of segment endpoints.
indices : array_like
Array of vertex indices: int pair or triple of indices from vertices array.
indices_type : {'segment', 'triangle'}, optional
Interpretation of indices array. 'segment' for pairs, 'triangle' for triples. Default is 'triangle'.
color : int, optional
Packed RGB color of the lines (0xff0000 is red, 0xff is blue) when `colors` is empty. Default is _default_color.
colors : array_like, optional
Array of int: packed RGB colors (0xff0000 is red, 0xff is blue) when attribute,
color_map and color_range are empty. Default is [].
attribute : array_like, optional
Array of float attribute for the color mapping, corresponding to each vertex. Default is [].
color_map : list, optional
A list of float quadruplets (attribute value, R, G, B), sorted by attribute value.
The first quadruplet should have value 0.0, the last 1.0; R, G, B are RGB color
components in the range 0.0 to 1.0. Default is default_colormap.
color_range : list, optional
A pair [min_value, max_value], which determines the levels of color attribute
mapped to 0 and 1 in the color map respectively. Default is [].
width : float, optional
Thickness of the lines. Default is 0.01.
shader : {'simple', 'thick', 'mesh'}, optional
Display style (name of the shader used) of the lines. Default is 'thick'.
roughness : float, optional
Roughness of object material. Default is 0.4.
metalness : float, optional
Metalness of object material. Default is 0.0.
radial_segments : int, optional
Number of segmented faces around the circumference of the tube. Default is 8.
opacity : float, optional
Opacity of lines. Default is 1.0.
name : str, optional
A name of the object. Default is None.
group : str, optional
A name of a group. Default is None.
custom_data : dict, optional
An object with custom data attached to object. Default is None.
compression_level : int, optional
Level of compression [-1, 9]. Default is 0.
visible : bool, optional
Whether the object is drawn. Default is True.
**kwargs
Additional keyword arguments passed to process_transform_arguments.
Returns
-------
Lines
The created Lines drawable object.
"""
if colors is None:
colors = []
if attribute is None:
attribute = []
if color_range is None:
color_range = []
if color_map is None:
color_map = default_colormap
color_map = (
np.array(color_map, np.float32) if type(color_map) is not dict else color_map
)
attribute = (
np.array(attribute, np.float32) if type(attribute) is not dict else attribute
)
color_range = check_attribute_color_range(attribute, color_range)
_warn_unlit_line(shader, roughness, metalness)
return process_transform_arguments(
Lines(
vertices=vertices,
indices=indices,
indices_type=indices_type,
color=color,
width=width,
shader=shader,
roughness=roughness,
metalness=metalness,
shininess=shininess,
radial_segments=radial_segments,
colors=colors,
attribute=attribute,
color_map=color_map,
color_range=color_range,
opacity=opacity,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
visible=visible,
),
**kwargs,
)
[docs]
def line(
vertices: ArrayLike,
color: int = _default_color,
colors: TypingList[int] = None, # lgtm [py/similar-function]
attribute: ArrayLike = None,
color_map: Optional[ColorMap] = None,
color_range: ColorRange = None,
width: float = 0.01,
opacity: float = 1.0,
shader: str = "thick",
roughness: float = 0.4,
metalness: float = 0.0,
shininess: float = None,
radial_segments: int = 8,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
visible: bool = True,
**kwargs: Any,
) -> Line:
"""
Create a Line drawable for plotting segments and polylines.
Parameters
----------
vertices : array_like
Array with (x, y, z) coordinates of segment endpoints.
color : int, optional
Hex color of the lines when `colors` is empty, by default _default_color.
colors : list, optional
Array of Hex colors when attribute, color_map and color_range are empty, by default [].
attribute : list, optional
List of values used to apply `color_map`, by default [].
color_map : list, optional
List of `float` quadruplets (attribute value, R, G, B) sorted by attribute value,
by default None. The first quadruplet should have value 0.0, the last 1.0; R, G, B
are RGB color components in the range 0.0 to 1.0.
color_range : list, optional
[min_value, max_value] pair determining the levels of color attribute mapped to 0
and 1 in the colormap, by default [].
width : float, optional
Thickness of the lines, by default 0.01.
shader : {'simple', 'thick', 'mesh'}, optional
Display style of the lines, by default 'thick'.
roughness : float, optional
Roughness of object material, by default 0.4.
metalness : float, optional
Metalness of object material, by default 0.0.
radial_segments : int, optional
Number of segmented faces around the circumference of the tube, by default 8.
name : str, optional
Object name, by default None.
group : str, optional
Name of a group, by default None.
custom_data : dict, optional
An object with custom data attached to object, by default None.
compression_level : int, optional
Level of data compression [-1, 9], by default 0.
visible : bool, optional
Whether the object is drawn. Default is True.
**kwargs
Additional keyword arguments passed to process_transform_arguments.
Returns
-------
Line
The created Line drawable object.
"""
if colors is None:
colors = []
if attribute is None:
attribute = []
if color_range is None:
color_range = []
if color_map is None:
color_map = default_colormap
color_map = (
np.array(color_map, np.float32) if type(color_map) is not dict else color_map
)
attribute = (
np.array(attribute, np.float32) if type(attribute) is not dict else attribute
)
color_range = check_attribute_color_range(attribute, color_range)
_warn_unlit_line(shader, roughness, metalness)
return process_transform_arguments(
Line(
vertices=vertices,
color=color,
width=width,
shader=shader,
radial_segments=radial_segments,
colors=colors,
attribute=attribute,
color_map=color_map,
color_range=color_range,
opacity=opacity,
roughness=roughness,
metalness=metalness,
shininess=shininess,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
visible=visible,
),
**kwargs,
)
[docs]
def mesh(
vertices: ArrayLike,
indices: ArrayLike,
normals: ArrayLike = None,
color: Optional[int] = None,
colors: TypingList[int] = None,
opacities: ArrayLike = None,
attribute: ArrayLike = None,
color_map: Optional[ColorMap] = None,
# lgtm [py/similar-function]
color_range: ColorRange = None,
wireframe: bool = False,
flat_shading: bool = True,
roughness: float = 0.4,
metalness: float = 0.0,
shininess: float = None,
opacity: float = 1.0,
texture: Optional[bytes] = None,
texture_file_format: Optional[str] = None,
volume: ArrayLike = None,
volume_bounds: ArrayLike = None,
opacity_function: ArrayLike = None,
side: str = "front",
uvs: Optional[ArrayLike] = None,
uvs2: Optional[ArrayLike] = None,
texture_wrap: str = "clamp",
emissive: int = 0,
emissive_intensity: float = 1.0,
emissive_map: Optional[bytes] = None,
normal_map: Optional[bytes] = None,
normal_scale: float = 1.0,
metalness_roughness_map: Optional[bytes] = None,
occlusion_map: Optional[bytes] = None,
occlusion_strength: float = 1.0,
alpha_mode: Optional[str] = None,
alpha_cutoff: float = 0.5,
transmission: float = 0.0,
ior: float = 1.5,
thickness: float = 0.0,
attenuation_color: int = 0xFFFFFF,
attenuation_distance: float = 0.0,
slice_planes: ArrayLike = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
triangles_attribute: ArrayLike = None,
visible: bool = True,
click_callback: Optional[Callable] = None,
hover_callback: Optional[Callable] = None,
**kwargs: Any,
) -> Mesh:
"""Create a Mesh drawable from 3D triangles.
Parameters
----------
vertices : array_like
Array of triangle vertices, `float` (x, y, z) coordinate triplets.
indices : array_like
Array of vertex indices. `int` triplets of indices from vertices array.
normals: array_like, optional
Array of vertex normals: float (x, y, z) coordinate triples. Normals are used when flat_shading is false.
If the normals are not specified here, normals will be automatically computed.
color : int, optional
Hex color of the mesh. It multiplies `colors`, the colormap and `texture`, the way a
base colour does in other renderers. By default _default_color, or white when any of
them is given.
.. versionchanged:: 3.2.0
`color` used to be ignored when `colors`, a colormap or a texture was given.
colors : array_like, optional
Colors per vertex: packed hex ints, or an (N, 3) or (N, 4) array of RGB(A) - floats
in 0..1 or integers in 0..255. The fourth column becomes `opacities`. By default [].
opacities : array_like, optional
Alpha per vertex, multiplied into the colour; read when `alpha_mode` is 'blend' or
'mask', by default [].
attribute: list, optional
List of values used to apply `color_map`, by default [].
color_map : list, optional
List of `float` quadruplets (attribute value, R, G, B) sorted by attribute value, by default None.
The first quadruplet should have value 0.0, the last 1.0;
R, G, B are RGB color components in the range 0.0 to 1.0.
color_range : list, optional
[min_value, max_value] pair determining the levels of color attribute mapped
to 0 and 1 in the colormap, by default [].
wireframe : bool, optional
Display the mesh as wireframe, by default False.
flat_shading : bool, optional
Display the mesh with flat shading, by default True.
roughness: `float`.
Roughness of object material.
metalness: `float`.
Metalness of object material.
opacity : float, optional
Opacity of the mesh, by default 1.0.
texture : bytes, optional
Image data in a specific format, by default None.
texture_file_format : str, optional
Format of the data, by default None - read from the data itself.
It should be the second part of MIME format of type 'image/',e.g. 'jpeg', 'png', 'gif', 'tiff'.
volume : list, optional
3D array of `float`, by default [].
volume_bounds : list, optional
6-element tuple specifying the bounds of the volume data (x0, x1, y0, y1, z0, z1), by default [].
opacity_function : list, optional
`float` tuples (attribute value, opacity) sorted by attribute value, by default [].
The first tuples should have value 0.0, the last 1.0; opacity is in the range 0.0 to 1.0.
side : {'front', 'back', 'double'}, optional
Side to render, by default "front".
uvs : array_like, optional
float uvs for the texturing corresponding to each vertex, by default None.
uvs2 : array_like, optional
A second set of uvs, read by `occlusion_map` alone; it falls back to `uvs`, by default None.
texture_wrap : str, optional
What every texture of the mesh does outside 0..1: 'clamp', 'repeat' or 'mirror', or
one for u and one for v, e.g. 'repeat clamp'. By default 'clamp'. The cinematic
renderer repeats every texture.
emissive : int, optional
Hex color the surface emits, unaffected by lighting, by default 0 (none).
emissive_intensity : float, optional
Multiplier of `emissive`, by default 1.0.
emissive_map : bytes, optional
Image (PNG, JPEG, WebP, GIF) multiplying `emissive`, by default None.
normal_map : bytes, optional
Tangent-space normal map image, read with `uvs`, by default None.
normal_scale : float, optional
Strength of `normal_map`, by default 1.0.
metalness_roughness_map : bytes, optional
Image whose green channel multiplies `roughness` and blue channel `metalness` - the
glTF layout, by default None.
occlusion_map : bytes, optional
Image whose red channel darkens the indirect light, read with `uvs2` when given,
by default None.
occlusion_strength : float, optional
How much of `occlusion_map` applies, 0 to 1, by default 1.0.
alpha_mode : {'opaque', 'blend', 'mask'}, optional
How the alpha of `texture` and `opacities` is used: ignored ('opaque'), blended with
what is behind ('blend'), or cut out below `alpha_cutoff` ('mask'). `opacity` fades
the mesh in every mode. By default 'blend' when `opacities` are given, 'opaque' otherwise.
alpha_cutoff : float, optional
Threshold of the 'mask' mode, by default 0.5.
transmission : float, optional
How much light passes through the surface, refracted - glass, water, gems - from 0
to 1, by default 0.
ior : float, optional
Index of refraction of a transmissive mesh, by default 1.5.
thickness : float, optional
Thickness of the volume behind a transmissive surface, by default 0 (a thin wall).
attenuation_color : int, optional
Hex colour light takes on after `attenuation_distance` in the volume, by default white.
attenuation_distance : float, optional
Distance after which light has the `attenuation_color`, by default 0 (no attenuation).
name : str, optional
Object name, by default None.
group : str, optional
Name of a group, by default None.
custom_data: `dict`
A object with custom data attached to object.
compression_level : int, optional
Level of data compression [-1, 9], by default 0.
triangles_attribute : list, optional
Array of float attribute for the color mapping, one value per triangle rather than per
vertex; used when `attribute` is empty, by default [].
slice_planes : list, optional
Planes [a, b, c, d] the section outline is drawn along, up to eight of them. The outline
is drawn in the object colour, without the colormap, by default [].
visible : bool, optional
Whether the object is drawn. Default is True.
click_callback : callable, optional
Called with the picking parameters when the object is clicked, while the plot is
in mode='callback'. Default is None.
hover_callback : callable, optional
Called with the picking parameters when the cursor is over the object, while the
plot is in mode='callback'. Default is None.
**kwargs
For other keyword-only arguments, see :ref:`process_transform_arguments`.
Returns
-------
Mesh
Mesh Drawable
"""
# Ensure arraylike attributes are initialized as [] if None
if colors is None:
colors = []
if slice_planes is None:
slice_planes = []
if normals is None:
normals = []
if attribute is None:
attribute = []
if triangles_attribute is None:
triangles_attribute = []
if volume is None:
volume = []
if volume_bounds is None:
volume_bounds = []
if opacity_function is None:
opacity_function = []
if uvs is None:
uvs = []
if uvs2 is None:
uvs2 = []
if opacities is None:
opacities = []
colors, alpha = pack_colors(colors)
if alpha is not None:
if has_data(opacities):
raise ValueError("opacities given twice: as the fourth column of colors and as opacities")
opacities = alpha
if alpha_mode is None:
alpha_mode = "blend" if has_data(opacities) else "opaque"
if texture is not None and texture_file_format is None:
texture_file_format = image_format(texture)
color = factory_color(color, colors, attribute, triangles_attribute, texture)
if color_map is None:
color_map = default_colormap
color_map = (
np.array(color_map, np.float32) if type(color_map) is not dict else color_map
)
uvs = np.array(uvs, np.float32) if type(uvs) is not dict else uvs
uvs2 = np.array(uvs2, np.float32) if type(uvs2) is not dict else uvs2
opacities = np.array(opacities, np.float32) if type(opacities) is not dict else opacities
attribute = (
np.array(attribute, np.float32) if type(attribute) is not dict else attribute
)
normals = np.array(normals, np.float32) if type(normals) is not dict else normals
triangles_attribute = (
np.array(triangles_attribute, np.float32)
if type(triangles_attribute) is not dict
else triangles_attribute
)
volume_bounds = (
np.array(volume_bounds, np.float32)
if type(volume_bounds) is not dict
else volume_bounds
)
if len(attribute) > 0:
color_range = check_attribute_color_range(attribute, color_range)
if len(triangles_attribute) > 0:
color_range = check_attribute_color_range(triangles_attribute, color_range)
if len(volume) > 0:
color_range = check_attribute_color_range(volume, color_range)
return process_transform_arguments(
Mesh(
vertices=vertices,
indices=indices,
normals=normals,
color=color,
colors=colors,
attribute=attribute,
triangles_attribute=triangles_attribute,
color_map=color_map,
color_range=color_range,
wireframe=wireframe,
flat_shading=flat_shading,
roughness=roughness,
metalness=metalness,
shininess=shininess,
opacity=opacity,
volume=volume,
volume_bounds=volume_bounds,
opacity_function=opacity_function,
side=side,
texture=texture,
uvs=uvs,
uvs2=uvs2,
texture_file_format=texture_file_format,
texture_wrap=texture_wrap,
opacities=opacities,
emissive=emissive,
emissive_intensity=emissive_intensity,
emissive_map=emissive_map,
normal_map=normal_map,
normal_scale=normal_scale,
metalness_roughness_map=metalness_roughness_map,
occlusion_map=occlusion_map,
occlusion_strength=occlusion_strength,
alpha_mode=alpha_mode,
alpha_cutoff=alpha_cutoff,
transmission=transmission,
ior=ior,
thickness=thickness,
attenuation_color=attenuation_color,
attenuation_distance=attenuation_distance,
slice_planes=slice_planes,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
visible=visible,
click_callback=click_callback,
hover_callback=hover_callback,
),
**kwargs,
)
# noinspection PyShadowingNames
[docs]
def stl(
stl: Union[str, bytes],
color: int = _default_color,
wireframe: bool = False,
flat_shading: bool = True,
roughness: float = 0.4,
metalness: float = 0.0,
shininess: float = None,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
visible: bool = True,
**kwargs: Any,
) -> STL:
"""Create an STL drawable for data in STereoLitograpy format.
Parameters
----------
stl : `str` or `bytes`
STL data in either ASCII STL (`str`) or Binary STL (`bytes`).
color : int, optional
Hex color of the mesh, by default _default_color.
wireframe : bool, optional
Display the mesh as wireframe, by default False.
flat_shading : bool, optional
Display the mesh with flat shading, by default True.
roughness: `float`.
Roughness of object material.
metalness: `float`.
Metalness of object material.
name : str, optional
Object name, by default None.
group : str, optional
Name of a group, by default None.
custom_data: `dict`
A object with custom data attached to object.
compression_level : int, optional
Level of data compression [-1, 9], by default 0.
visible : bool, optional
Whether the object is drawn. Default is True.
**kwargs
For other keyword-only arguments, see :ref:`process_transform_arguments`.
Returns
-------
STL
STL Drawable.
"""
plain = isinstance(stl, str)
return process_transform_arguments(
STL(
text=stl if plain else None,
binary=stl if not plain else None,
color=color,
wireframe=wireframe,
flat_shading=flat_shading,
roughness=roughness,
metalness=metalness,
shininess=shininess,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
visible=visible,
),
**kwargs,
)
[docs]
def surface(
heights: ArrayLike,
color: int = _default_color,
wireframe: bool = False,
flat_shading: bool = True,
roughness: float = 0.4,
metalness: float = 0.0,
shininess: float = None,
attribute: ArrayLike = None,
color_map: Optional[ColorMap] = None,
color_range: ColorRange = None,
opacity: float = 1.0,
name: Optional[str] = None,
group: Optional[str] = None,
custom_data: Optional[TypingDict[str, Any]] = None,
compression_level: int = 0,
visible: bool = True,
click_callback: Optional[Callable] = None,
hover_callback: Optional[Callable] = None,
**kwargs: Any,
) -> Surface:
"""Create a Surface drawable.
Plot a 2d function: z = f(x, y).
The default domain of the scalar field is -0.5 < x, y < 0.5.
If the domain should be different, the bounding box needs to be transformed using `kwargs`
- ``surface(..., bounds=[-1, 1, -1, 1])``
- ``surface(..., xmin=-10, xmax=10, ymin=-4, ymax=4)``
Parameters
----------
heights : array_like
Array of `float` values.
color : int, optional
Hex color of the surface, by default _default_color.
wireframe : bool, optional
Display the mesh as wireframe, by default False.
flat_shading : bool, optional
Display the mesh with flat shading, by default True.
roughness: `float`.
Roughness of object material.
metalness: `float`.
Metalness of object material.
attribute: list, optional
List of values used to apply `color_map`, by default [].
opacity: `float`.
Opacity of surface.
color_map : list, optional
List of `float` quadruplets (attribute value, R, G, B) sorted by attribute value, by default None.
The first quadruplet should have value 0.0, the last 1.0;
R, G, B are RGB color components in the range 0.0 to 1.0.
color_range : list, optional
[min_value, max_value] pair determining the levels of color attribute mapped
to 0 and 1 in the colormap, by default [].
name : str, optional
Object name, by default None.
group : str, optional
Name of a group, by default None.
custom_data: `dict`
A object with custom data attached to object.
compression_level : int, optional
Level of data compression [-1, 9], by default 0.
visible : bool, optional
Whether the object is drawn. Default is True.
click_callback : callable, optional
Called with the picking parameters when the object is clicked, while the plot is
in mode='callback'. Default is None.
hover_callback : callable, optional
Called with the picking parameters when the cursor is over the object, while the
plot is in mode='callback'. Default is None.
**kwargs
For other keyword-only arguments, see :ref:`process_transform_arguments`.
Returns
-------
Surface
Surface Drawable.
"""
if attribute is None:
attribute = []
if color_range is None:
color_range = []
if color_map is None:
color_map = default_colormap
color_map = (
np.array(color_map, np.float32) if type(color_map) is not dict else color_map
)
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(
Surface(
heights=heights,
color=color,
wireframe=wireframe,
flat_shading=flat_shading,
roughness=roughness,
metalness=metalness,
shininess=shininess,
attribute=attribute,
color_map=color_map,
color_range=color_range,
opacity=opacity,
name=name,
group=group,
custom_data=custom_data,
compression_level=compression_level,
visible=visible,
click_callback=click_callback,
hover_callback=hover_callback,
),
**kwargs,
)