Source code for fury.material

"""Module for creating various materials used in 3D rendering."""

import numpy as np

from fury.lib import (
    ImageBasicMaterial,
    LineArrowMaterial,
    LineMaterial,
    LineSegmentMaterial,
    LineThinMaterial,
    LineThinSegmentMaterial,
    MeshBasicMaterial,
    MeshPhongMaterial,
    PointsGaussianBlobMaterial,
    PointsMarkerMaterial,
    PointsMaterial,
    TextMaterial,
)


[docs] def validate_opacity(opacity): """ Ensure opacity is between 0 and 1. Parameters ---------- opacity : float Opacity value to validate. Returns ------- float Validated opacity value. Raises ------ ValueError If opacity is not between 0 and 1. """ if opacity is None: return 1.0 if not (0 <= opacity <= 1): raise ValueError("Opacity must be between 0 and 1.") return opacity
[docs] def validate_color(color, opacity, mode): """ Validate and modify color based on opacity and mode. Parameters ---------- color : tuple or None RGB or RGBA color tuple. opacity : float Opacity value between 0 and 1. mode : str Color mode, either 'auto' or 'vertex'. Returns ------- tuple or None Modified color tuple with opacity applied. Raises ------ ValueError If color is None when mode is 'auto' or if color has invalid length. """ if color is None and mode == "auto": return (1, 1, 1, opacity) if mode == "vertex": return (1, 1, 1) if color is not None: if len(color) == 3: return (*color, opacity) elif len(color) == 4: return (*color[:3], color[3] * opacity) else: raise ValueError("Color must be a tuple of length 3 or 4.") return color
def _create_mesh_material( *, material="phong", enable_picking=True, color=None, opacity=1.0, mode="vertex", flat_shading=True, texture=None, wireframe=False, wireframe_thickness=-1.0, alpha_mode="blend", depth_write=True, ): """ Create a mesh material. Parameters ---------- material : str, optional The type of material to create. Options are 'phong' (default) and 'basic'. enable_picking : bool, optional Whether the material should be pickable in a scene. color : tuple or None, optional The color of the material, represented as an RGB or RGBA tuple. If None, the default color is used. opacity : float, optional The opacity of the material, from 0 (transparent) to 1 (opaque). If RGBA is provided, the final alpha will be: final_alpha = alpha_in_RGBA * opacity. mode : str, optional The color mode of the material. Options are 'auto' and 'vertex'. flat_shading : bool, optional Whether to use flat shading (True) or smooth shading (False). texture : Texture or TextureMap, optional The texture map specifying the color for each texture coordinate. wireframe : bool, optional Whether to render the mesh as a wireframe. wireframe_thickness : float, optional The thickness of the wireframe lines. alpha_mode : str, optional The alpha mode for the material. Please see the below link for details: https://docs.pygfx.org/stable/_autosummary/materials/pygfx.materials.Material.html#pygfx.materials.Material.alpha_mode. depth_write : bool, optional Whether to write depth information for the material. Returns ------- MeshMaterial A mesh material object of the specified type with the given properties. Raises ------ ValueError If an unsupported material type is specified. """ opacity = validate_opacity(opacity) color = validate_color(color, opacity, mode) args = { "pick_write": enable_picking, "color_mode": mode, "color": color, "opacity": opacity, "flat_shading": flat_shading, "map": texture, "wireframe": wireframe, "wireframe_thickness": wireframe_thickness, "alpha_mode": alpha_mode, "depth_write": depth_write, } if material == "phong": return MeshPhongMaterial(**args) elif material == "basic": return MeshBasicMaterial(**args) else: raise ValueError(f"Unsupported material type: {material}") def _create_line_material( *, material="basic", enable_picking=True, color=None, opacity=1.0, mode="auto", thickness=2.0, thickness_space="screen", dash_pattern=(), dash_offset=0.0, anti_aliasing=True, alpha_mode="blend", depth_write=True, ): """ Create a line material. Parameters ---------- material : str, optional The type of line material to create. Options are 'line' (default), 'segment', 'arrow', 'thin', and 'thin_segment'. enable_picking : bool, optional Whether the material should be pickable in a scene. color : tuple or None, optional The color of the material, represented as an RGBA tuple. If None, the default color is used. opacity : float, optional The opacity of the material, from 0 (transparent) to 1 (opaque). If RGBA is provided, the final alpha will be: final_alpha = alpha_in_RGBA * opacity. mode : str, optional The color mode of the material. Options are 'auto' and 'vertex'. thickness : float, optional The line thickness expressed in logical pixels. thickness_space : str, optional The coordinate space in which the thickness is expressed ('screen', 'world', 'model'). dash_pattern : tuple, optional The pattern of the dash, e.g., [2, 3]. meaning no dashing. dash_offset : float, optional The offset into the dash cycle to start drawing at. anti_aliasing : bool, optional Whether or not the line is anti-aliased in the shader. alpha_mode : str, optional The alpha mode for the material. Please see the below link for details: https://docs.pygfx.org/stable/_autosummary/materials/pygfx.materials.Material.html#pygfx.materials.Material.alpha_mode. depth_write : bool, optional Whether to write depth information for the material. Returns ------- LineMaterial A line material object of the specified type with the given properties. """ opacity = validate_opacity(opacity) color = validate_color(color, opacity, mode) args = { "pick_write": enable_picking, "color_mode": mode, "color": color, "thickness": thickness, "thickness_space": thickness_space, "dash_pattern": dash_pattern, "dash_offset": dash_offset, "aa": anti_aliasing, "alpha_mode": alpha_mode, "depth_write": depth_write, } if material == "basic": return LineMaterial(**args) elif material == "segment": return LineSegmentMaterial(**args) elif material == "arrow": return LineArrowMaterial(**args) elif material == "thin": return LineThinMaterial(**args) elif material == "thin_segment": return LineThinSegmentMaterial(**args) else: raise ValueError(f"Unsupported material type: {material}") def _create_vector_field_material( cross_section, *, visibility=None, material="thin_line", enable_picking=True, opacity=1.0, thickness=1.0, thickness_space="screen", anti_aliasing=True, alpha_mode="blend", depth_write=True, ): """ Create a line material. Parameters ---------- cross_section : list or tuple, shape (3,), optional A list or tuple representing the cross section dimensions. If None, the cross section will be ignored and complete field will be shown. visibility : list or tuple, shape (3,), optional A list or tuple representing the visibility in the x, y, and z dimensions. If None, the visibility will be set to (-1, -1, -1) to show the complete field. material : str, optional The type of vector field material to create. Options are 'thin_line' (default), 'line', 'arrow'. enable_picking : bool, optional Whether the material should be pickable in a scene. opacity : float, optional The opacity of the material, from 0 (transparent) to 1 (opaque). If RGBA is provided, the final alpha will be: final_alpha = alpha_in_RGBA * opacity. thickness : float, optional The line thickness expressed in logical pixels. thickness_space : str, optional The coordinate space in which the thickness is expressed ('screen', 'world', 'model'). anti_aliasing : bool, optional Whether or not the line is anti-aliased in the shader. alpha_mode : str, optional The alpha mode for the material. Please see the below link for details: https://docs.pygfx.org/stable/_autosummary/materials/pygfx.materials.Material.html#pygfx.materials.Material.alpha_mode. depth_write : bool, optional Whether to write depth information for the material. Returns ------- LineMaterial A line material object of the specified type with the given properties. """ opacity = validate_opacity(opacity) args = { "pick_write": enable_picking, "opacity": opacity, "thickness": thickness, "thickness_space": thickness_space, "aa": anti_aliasing, "alpha_mode": alpha_mode, "depth_write": depth_write, } if material == "thin_line": return VectorFieldThinLineMaterial(cross_section, visibility=visibility, **args) elif material == "line": return VectorFieldLineMaterial(cross_section, visibility=visibility, **args) elif material == "arrow": return VectorFieldArrowMaterial(cross_section, visibility=visibility, **args) else: raise ValueError(f"Unsupported material type: {material}") def _create_points_material( *, material="basic", color=(1.0, 1.0, 1.0), size=4.0, map=None, aa=True, marker="circle", edge_color="black", edge_width=1.0, mode="vertex", opacity=1.0, enable_picking=True, alpha_mode="blend", depth_write=True, ): """ Create a points material. Parameters ---------- material : str, optional The type of material to create. Options are 'basic' (default), 'gaussian', and 'marker'. color : tuple, optional RGB or RGBA values in the range [0, 1]. size : float, optional The size (diameter) of the points in logical pixels. map : TextureMap or Texture, optional The texture map specifying the color for each texture coordinate. aa : bool, optional Whether or not the points are anti-aliased in the shader. marker : str or MarkerShape, optional The shape of the marker. Options are "●": "circle", "+": "plus", "x": "cross", "♥": "heart", "✳": "asterix". edge_color : str or tuple or Color, optional The color of line marking the edge of the markers. edge_width : float, optional The width of the edge of the markers. mode : str, optional The color mode of the material. Options are 'auto' and 'vertex'. opacity : float, optional The opacity of the material, from 0 (transparent) to 1 (opaque). If RGBA is provided, the final alpha will be: final_alpha = alpha_in_RGBA * opacity. enable_picking : bool, optional Whether the material should be pickable in a scene. alpha_mode : str, optional The alpha mode for the material. Please see the below link for details: https://docs.pygfx.org/stable/_autosummary/materials/pygfx.materials.Material.html#pygfx.materials.Material.alpha_mode. depth_write : bool, optional Whether to write depth information for the material. Returns ------- PointsMaterial A point material object of the specified type with the given properties. Raises ------ ValueError If an unsupported material type is specified. """ opacity = validate_opacity(opacity) color = validate_color(color, 1.0, mode) args = { "color": color, "size": size, "color_mode": mode, "map": map, "aa": aa, "pick_write": enable_picking, "opacity": opacity, "alpha_mode": alpha_mode, "depth_write": depth_write, } if material == "basic": return PointsMaterial(**args) elif material == "gaussian": return PointsGaussianBlobMaterial(**args) elif material == "marker": return PointsMarkerMaterial( marker=marker, edge_color=edge_color, edge_width=edge_width, **args ) else: raise ValueError(f"Unsupported material type: {material}") def _create_text_material( *, color=(0, 0, 0), opacity=1.0, outline_color=(0, 0, 0), outline_thickness=0.0, weight_offset=1.0, aliasing=True, alpha_mode="blend", depth_write=True, ): """ Create a text material. Parameters ---------- color : tuple, optional The color of the text as RGB or RGBA tuple. opacity : float, optional The opacity of the material, from 0 (transparent) to 1 (opaque). If RGBA is provided, the final alpha will be: final_alpha = alpha_in_RGBA * opacity. outline_color : tuple, optional The color of the outline of the text as RGB or RGBA tuple. outline_thickness : float, optional A value indicating the relative width of the outline. Valid values are between 0.0 and 0.5. weight_offset : float, optional A value representing an offset to the font weight. Font weights are in the range 100-900, so this value should be in the same order of magnitude. Can be negative to make text thinner. aliasing : bool, optional If True, use anti-aliasing while rendering glyphs. Aliasing gives prettier results, but may affect performance for very large texts. alpha_mode : str, optional The alpha mode for the material. Please see the below link for details: https://docs.pygfx.org/stable/_autosummary/materials/pygfx.materials.Material.html#pygfx.materials.Material.alpha_mode. depth_write : bool, optional Whether to write depth information for the material. Returns ------- TextMaterial A text material object with the specified properties. """ opacity = validate_opacity(opacity) if color is not None: if len(color) == 3: color = (*color, opacity) elif len(color) == 4: color = color color = (*color[:3], color[3] * opacity) else: raise ValueError("Color must be a tuple of length 3 or 4.") return TextMaterial( color=color, outline_color=outline_color, outline_thickness=outline_thickness, weight_offset=weight_offset, aa=aliasing, alpha_mode=alpha_mode, depth_write=depth_write, ) def _create_image_material( *, clim=None, map=None, gamma=1.0, interpolation="nearest", alpha_mode="blend", depth_write=True, ): """ Rasterized image material. Parameters ---------- clim : tuple, optional The contrast limits to scale the data values with. map : Texture or TextureMap, optional The texture map to turn the image values into its final color. gamma : float, optional The gamma correction to apply to the image data. Must be greater than 0.0. interpolation : str, optional The method to interpolate the image data. Either 'nearest' or 'linear'. alpha_mode : str, optional The alpha mode for the material. Please see the below link for details: https://docs.pygfx.org/stable/_autosummary/materials/pygfx.materials.Material.html#pygfx.materials.Material.alpha_mode. depth_write : bool, optional Whether to write depth information for the material. Returns ------- ImageMaterial A rasterized image material object with the specified properties. """ return ImageBasicMaterial( clim=clim, map=map, gamma=gamma, interpolation=interpolation, alpha_mode=alpha_mode, depth_write=depth_write, )
[docs] class VectorFieldThinLineMaterial(LineMaterial): """ Material for VectorFieldActor. Parameters ---------- cross_section : {list, tuple, ndarray} A list or tuple or ndarray representing the cross section dimensions. visibility : {list, tuple, ndarray}, optional A list or tuple or ndarray representing the visibility in the 3D. If None, the visibility will be set to (-1, -1, -1) to show the complete field. **kwargs : dict Additional keyword arguments for the material. """ uniform_type = dict( LineThinSegmentMaterial.uniform_type, cross_section="4xf4", # vec4<i32> visibility="4xi4", # vec4<i32> )
[docs] def __init__(self, cross_section, *, visibility=None, **kwargs): """ Initialize the VectorFieldMaterial. Parameters ---------- cross_section : {list, tuple, ndarray} A list or tuple or ndarray representing the cross section dimensions. visibility : {list, tuple, ndarray}, optional A list or tuple or ndarray representing the visibility in the 3D. If None, the visibility will be set to (-1, -1, -1) to show the complete field. **kwargs : dict Additional keyword arguments for the material. """ super().__init__(color_mode="vertex", **kwargs) self.cross_section = cross_section self.visibility = visibility
@property def visibility(self): """ Get the visibility of the vector field in each dimension. Returns ------- list A list representing the visibility in the x, y, and z dimensions. """ vis = self.uniform_buffer.data["visibility"][:3] if all(vis == (-1, -1, -1)): return None return [bool(i) for i in vis] @visibility.setter def visibility(self, visibility): """ Set the visibility of the vector field in each dimension. Parameters ---------- visibility : list or tuple A list or tuple representing the visibility in the x, y, and z dimensions. """ if visibility is None: self.uniform_buffer.data["visibility"] = np.asarray( [-1, -1, -1, 0], dtype=np.int32 ) else: if len(visibility) != 3: raise ValueError("visibility must have exactly 3 dimensions.") if not all( isinstance(i, bool) or (hasattr(i, "item") and isinstance(i.item(), bool)) for i in visibility ): raise ValueError("visibility must contain only booleans.") self.uniform_buffer.data["visibility"] = np.asarray( [*visibility, 0], dtype=np.int32 ) self.uniform_buffer.update_full() @property def cross_section(self): """ Get the cross section of the vector field. Returns ------- list A list representing the cross section dimensions. """ return self.uniform_buffer.data["cross_section"][:3] @cross_section.setter def cross_section(self, cross_section): """ Set the cross section of the vector field. Parameters ---------- cross_section : list or tuple A list or tuple representing the cross section dimensions. """ if len(cross_section) != 3: raise ValueError("cross_section must have exactly 3 dimensions.") self.uniform_buffer.data["cross_section"] = np.asarray( [*cross_section, 0], dtype=np.int32 ) self.uniform_buffer.update_full()
[docs] class VectorFieldLineMaterial(VectorFieldThinLineMaterial): """ Material for VectorFieldActor. This class provides a way to distinguish the usage of right shader for creating a vector field. """
[docs] class VectorFieldArrowMaterial(VectorFieldThinLineMaterial): """ Material for VectorFieldActor. This class provides a way to distinguish the usage of right shader for creating a vector field. """
[docs] class SphGlyphMaterial(MeshPhongMaterial): """ Initialize the Spherical Glyph Material. Parameters ---------- n_coeffs : int, optional The maximum spherical harmonic degree. scale : int, optional The scale factor. shininess : int, optional The shininess factor. emissive : str, optional The emissive color. specular : str, optional The specular color. **kwargs : dict Additional keyword arguments for the material. """ uniform_type = dict( MeshPhongMaterial.uniform_type, n_coeffs="i4", scale="f4", )
[docs] def __init__( self, n_coeffs=-1, scale=1, shininess=30, emissive="#000", specular="#494949", **kwargs, ): """ Initialize the Spherical Glyph Material. Parameters ---------- n_coeffs : int, optional The maximum spherical harmonic degree. This value will limit the number of spherical harmonic coefficients that can be used from the data. If -1, no limit is applied. scale : int, optional The scale factor. shininess : int, optional The shininess factor. emissive : str, optional The emissive color. specular : str, optional The specular color. **kwargs : dict Additional keyword arguments for the material. """ super().__init__(shininess, emissive, specular, **kwargs) self.n_coeffs = n_coeffs self.scale = scale
@property def n_coeffs(self): """ Get the maximum number of spherical harmonic coefficients. Returns ------- int The maximum number of spherical harmonic coefficients. """ return self.uniform_buffer.data["n_coeffs"] @n_coeffs.setter def n_coeffs(self, value): """ Set the maximum number of spherical harmonic coefficients. Parameters ---------- value : int The maximum number of spherical harmonic coefficients. """ if not isinstance(value, int): raise ValueError("n_coeffs must be an integer.") self.uniform_buffer.data["n_coeffs"] = value self.uniform_buffer.update_full() @property def scale(self): """ Get the scale factor. Returns ------- float The scale factor. """ return self.uniform_buffer.data["scale"] @scale.setter def scale(self, value): """ Set the scale factor. Parameters ---------- value : float The scale factor. """ if not isinstance(value, (int, float)): raise ValueError("scale must be a number.") self.uniform_buffer.data["scale"] = float(value) self.uniform_buffer.update_full()
[docs] class StreamlinesMaterial(LineMaterial): """ Initialize the Streamlines Material. Parameters ---------- outline_thickness : float, optional The thickness of the outline. outline_color : tuple, optional The color of the outline as an RGBA tuple. roi_enabled : bool, optional Whether ROI culling is active. This is typically driven by the presence of an ROI mask on the actor. roi_dim : tuple, optional 3D integer dimensions (nx, ny, nz) of a mask grid when a volumetric ROI is used in the shader. **kwargs : dict Additional keyword arguments for the material. """ uniform_type = dict( LineMaterial.uniform_type, outline_thickness="f4", outline_color="4xf4", roi_enabled="i4", roi_dim="4xi4", )
[docs] def __init__( self, outline_thickness=0.0, outline_color=(0, 0, 0), roi_enabled=None, roi_dim=(0, 0, 0), **kwargs, ): """ Initialize the Streamline Material. Parameters ---------- outline_thickness : float, optional The thickness of the outline. outline_color : tuple, optional The color of the outline as an RGBA tuple. roi_enabled : bool, optional Whether ROI culling is active. If None, ROI culling defaults to False until an ROI mask is attached by the actor. roi_dim : tuple, optional 3D integer dimensions (nx, ny, nz) of a mask grid when a volumetric ROI is used in the shader. **kwargs : dict Additional keyword arguments for the material. """ super().__init__(**kwargs) self.outline_thickness = outline_thickness self.outline_color = outline_color self.roi_dim = roi_dim self.roi_enabled = roi_enabled
@property def outline_thickness(self): """ Get the outline thickness. Returns ------- float The thickness of the outline. """ return float(self.uniform_buffer.data["outline_thickness"]) @outline_thickness.setter def outline_thickness(self, value): """ Set the outline thickness. Parameters ---------- value : float The thickness of the outline. """ self.uniform_buffer.data["outline_thickness"] = float(value) self.uniform_buffer.update_full() @property def outline_color(self): """ Get the outline color. Returns ------- tuple The color of the outline as an RGBA tuple. """ return self.uniform_buffer.data["outline_color"][:3] @outline_color.setter def outline_color(self, value): """ Set the outline color. Parameters ---------- value : tuple The color of the outline as an RGB or RGBA tuple. """ if len(value) == 3: value = (*value, 1.0) self.uniform_buffer.data["outline_color"] = value self.uniform_buffer.update_full() @property def roi_enabled(self): """ Return True when ROI-based culling is active. Returns ------- bool True if ROI-based culling is enabled, False otherwise. """ return bool(self.uniform_buffer.data["roi_enabled"]) @roi_enabled.setter def roi_enabled(self, value): """ Enable/disable ROI-based culling. Parameters ---------- value : bool True to enable ROI-based culling, False to disable. """ self.uniform_buffer.data["roi_enabled"] = int(bool(value)) self.uniform_buffer.update_full() @property def roi_dim(self): """ ROI grid dimensions as (nx, ny, nz). Returns ------- tuple A tuple of three integers representing the ROI grid dimensions. """ return tuple(int(x) for x in self.uniform_buffer.data["roi_dim"][:3]) @roi_dim.setter def roi_dim(self, value): """ Set the ROI grid dimensions. Parameters ---------- value : tuple A tuple of three integers representing the ROI grid dimensions. """ dims = np.asarray(value, dtype=np.int32).reshape(-1) if dims.size != 3: raise ValueError("roi_dim must contain exactly three integers.") self.uniform_buffer.data["roi_dim"] = ( int(dims[0]), int(dims[1]), int(dims[2]), 0, ) self.uniform_buffer.update_full()
class _StreamlineBakedMaterial(StreamlinesMaterial): """ Initialize the internal baked streamline material. Parameters ---------- auto_detach : bool, optional If True, automatically switch to render-only material after baking. **kwargs : dict Additional keyword arguments for the material. """ def __init__(self, *, auto_detach=True, **kwargs): """ Initialize the internal baked streamline material. Parameters ---------- auto_detach : bool, optional If True, automatically switch to render-only material after baking. **kwargs : dict Additional keyword arguments for the material. """ super().__init__(**kwargs) self.auto_detach = bool(auto_detach)
[docs] class BillboardMaterial(MeshBasicMaterial): """ Billboard material for creating quads that always face the camera. This material is designed to work with the BillboardShader to create rectangles that automatically rotate to face the camera while maintaining their 3D position. Parameters ---------- **kwargs : dict Additional keyword arguments forwarded to :class:`~fury.material.MeshBasicMaterial`. """
[docs] def __init__(self, **kwargs): """ Initialize the material and forward arguments to the base class. Parameters ---------- **kwargs : dict Additional keyword arguments forwarded to ``MeshBasicMaterial``. """ super().__init__(**kwargs)
[docs] class StreamtubeMaterial(MeshPhongMaterial): """ Material for GPU-generated streamtubes. Parameters ---------- **kwargs : dict, optional Arguments forwarded to :class:`MeshPhongMaterial`. """
[docs] def __init__(self, **kwargs): """ Initialise the material with MeshPhongMaterial keyword arguments. Parameters ---------- **kwargs : dict, optional Arguments forwarded to :class:`MeshPhongMaterial`. """ super().__init__(**kwargs)
class _StreamtubeBakedMaterial(MeshPhongMaterial): """ Internal material for compute-shader-based streamtubes with auto-baking. This material is used internally by the streamtube actor when GPU compute shaders are available. Users should not instantiate this directly; instead use the ``streamtube()`` function which will automatically select the appropriate implementation. Parameters ---------- radius : float, optional Tube radius used by the compute shader. segments : int, optional Number of radial segments forming the tube. end_caps : bool, optional Whether flat caps are generated for each tube. auto_detach : bool, optional If True, automatically switch to render-only material after baking. **kwargs : dict, optional Additional arguments forwarded to :class:`MeshPhongMaterial`. Notes ----- This is an internal class marked with a leading underscore. It should not be used directly by end users. """ uniform_type = dict( MeshPhongMaterial.uniform_type, tube_radius="f4", tube_segments="u4", tube_end_caps="i4", line_count="u4", ) def __init__( self, *, radius=0.2, segments=8, end_caps=True, auto_detach=True, **kwargs, ): """ Initialise uniforms controlling the compute-driven streamtube. Parameters ---------- radius : float, optional Tube radius used by the compute shader. segments : int, optional Number of radial segments forming the tube. end_caps : bool, optional Whether flat caps are generated for each tube. auto_detach : bool, optional If True, automatically switch to render-only material after baking. **kwargs : dict, optional Additional arguments forwarded to :class:`MeshPhongMaterial`. """ super().__init__(**kwargs) self.radius = radius self.segments = segments self.end_caps = end_caps self.line_count = 0 self.auto_detach = bool(auto_detach) @property def radius(self): """ Get the tube radius used by the compute shader. Returns ------- float Current tube radius. """ return float(self.uniform_buffer.data["tube_radius"]) @radius.setter def radius(self, value): """ Set the tube radius used in the compute shader. Parameters ---------- value : float New tube radius value. """ self.uniform_buffer.data["tube_radius"] = float(value) self.uniform_buffer.update_full() @property def segments(self): """ Get the number of radial segments per tube. Returns ------- int Number of radial segments. """ return int(self.uniform_buffer.data["tube_segments"]) @segments.setter def segments(self, value): """ Set the number of radial segments per tube. Parameters ---------- value : int New segment count. """ self.uniform_buffer.data["tube_segments"] = int(value) self.uniform_buffer.update_full() @property def end_caps(self): """ Check whether end caps are rendered. Returns ------- bool ``True`` when end caps are enabled. """ return bool(self.uniform_buffer.data["tube_end_caps"]) @end_caps.setter def end_caps(self, value): """ Enable or disable end caps on the tubes. Parameters ---------- value : bool Flag indicating whether to render end caps. """ self.uniform_buffer.data["tube_end_caps"] = int(bool(value)) self.uniform_buffer.update_full() @property def line_count(self): """ Get the number of lines processed by the compute shader. Returns ------- int Number of lines passed to the GPU compute stage. """ return int(self.uniform_buffer.data["line_count"]) @line_count.setter def line_count(self, value): """ Update the number of lines handled by the compute shader. Parameters ---------- value : int Number of lines to process. """ self.uniform_buffer.data["line_count"] = int(value) self.uniform_buffer.update_full() def _setup_compute_shader(self, line_count, max_line_length, tube_segments): """ Record metadata used by the GPU compute pass. Parameters ---------- line_count : int Total number of lines to process. max_line_length : int Maximum length of any line in the batch. tube_segments : int Number of radial tube segments. """ self.line_count = line_count self.segments = tube_segments self._max_line_length = max_line_length
[docs] class BillboardSphereMaterial(MeshPhongMaterial): """ Phong-lit material for billboard-based impostor spheres. Parameters ---------- **material_kwargs : dict Keyword arguments propagated to :class:`~fury.material.MeshPhongMaterial`. """
[docs] def __init__(self, **material_kwargs): """ Initialize the sphere impostor material. Parameters ---------- **material_kwargs : dict Keyword arguments propagated to :class:`~fury.material.MeshPhongMaterial`. """ material_kwargs.setdefault("flat_shading", False) super().__init__(**material_kwargs)