Source code for colour_visuals.grid

# !/usr/bin/env python
"""
Grid Visuals
============

Define the grid visuals:

-   :class:`colour_visuals.VisualGrid`
"""

from __future__ import annotations

import typing

import numpy as np
import pygfx as gfx
from colour.geometry import primitive_grid
from colour.plotting import CONSTANTS_COLOUR_STYLE

from colour_visuals.common import (
    DEFAULT_FLOAT_DTYPE_WGPU,
    append_channel,
    as_contiguous_array,
    conform_primitive_dtype,
)
from colour_visuals.visual import MixinPropertySize, Visual, visual_property

if typing.TYPE_CHECKING:
    from colour.hints import ArrayLike

__author__ = "Colour Developers"
__copyright__ = "Copyright 2023 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = ["VisualGrid"]


[docs] class VisualGrid(MixinPropertySize, Visual): """ Create a 3D grid. Parameters ---------- size Size of the grid. centred Whether to create the grid centred at the origin. major_grid_colours Colour of the major grid lines. minor_grid_colours Colour of the minor grid lines. major_tick_labels Whether to draw the major tick labels. major_tick_label_colours Colour of the major tick labels. minor_tick_labels Whether to draw the minor tick labels. minor_tick_label_colours Colour of the minor tick labels. Attributes ---------- - :attr:`~colour_visuals.VisualGrid.size` - :attr:`~colour_visuals.VisualGrid.centred` - :attr:`~colour_visuals.VisualGrid.major_grid_colours` - :attr:`~colour_visuals.VisualGrid.minor_grid_colours` - :attr:`~colour_visuals.VisualGrid.major_tick_labels` - :attr:`~colour_visuals.VisualGrid.major_tick_label_colours` - :attr:`~colour_visuals.VisualGrid.minor_tick_labels` - :attr:`~colour_visuals.VisualGrid.minor_tick_label_colours` Methods ------- - :meth:`~colour_visuals.VisualGrid.__init__` - :meth:`~colour_visuals.VisualGrid.update` Examples -------- >>> import os >>> from colour.utilities import suppress_stdout >>> from wgpu.gui.auto import WgpuCanvas >>> with suppress_stdout(): ... canvas = WgpuCanvas(size=(960, 540)) ... scene = gfx.Scene() ... scene.add( ... gfx.Background( ... None, gfx.BackgroundMaterial(np.array([0.18, 0.18, 0.18])) ... ) ... ) ... visual = VisualGrid() ... camera = gfx.PerspectiveCamera(50, 16 / 9) ... camera.show_object(visual, up=np.array([0, 0, 1]), scale=1.25) ... scene.add(visual) ... if os.environ.get("CI") is None: ... gfx.show(scene, camera=camera, canvas=canvas) .. image:: ../_static/Plotting_VisualGrid.png :align: center :alt: visual-grid """
[docs] def __init__( self, size: float = 20, centred: bool = True, major_grid_colours: ArrayLike = (0.5, 0.5, 0.5), minor_grid_colours: ArrayLike = (0.25, 0.25, 0.25), major_tick_labels: bool = True, major_tick_label_colours: ArrayLike = (0.75, 0.75, 0.75), minor_tick_labels: bool = True, minor_tick_label_colours: ArrayLike = (0.5, 0.5, 0.5), ) -> None: super().__init__() self._centred = True self._major_grid_colours = np.array([0.5, 0.5, 0.5]) self._minor_grid_colours = np.array([0.25, 0.25, 0.25]) self._major_tick_labels = True self._major_tick_label_colours = np.array([0.75, 0.75, 0.75]) self._minor_tick_labels = True self._minor_tick_label_colours = np.array([0.5, 0.5, 0.5]) self._axes_helper = None self._ticks_major_x = None self._ticks_major_y = None self._ticks_minor_x = None self._ticks_minor_y = None with self.block_update(): self.size = size self.centred = centred self.major_grid_colours = major_grid_colours self.minor_grid_colours = minor_grid_colours self.major_tick_labels = major_tick_labels self.major_tick_label_colours = major_tick_label_colours self.minor_tick_labels = minor_tick_labels self.minor_tick_label_colours = minor_tick_label_colours self.update()
@visual_property def centred(self) -> bool: """ Getter and setter property to create the grid centred at the origin. Parameters ---------- value Value to create the grid centred at the origin. Returns ------- :class:`bool` Create the grid centred at the origin. """ return self._centred @centred.setter def centred(self, value: bool) -> None: """Setter for the **self.centred** property.""" self._centred = value @visual_property def major_grid_colours(self) -> ArrayLike: """ Getter and setter property for the major grid colour. Parameters ---------- value Value to set the major grid colour with. Returns ------- ArrayLike Major grid colour. """ return self._major_grid_colours @major_grid_colours.setter def major_grid_colours(self, value: ArrayLike) -> None: """Setter for the **self.major_grid_colours** property.""" self._major_grid_colours = value @visual_property def minor_grid_colours(self) -> ArrayLike: """ Getter and setter property for the minor grid colour. Parameters ---------- value Value to set the minor grid colour with. Returns ------- ArrayLike Major grid colour. """ return self._minor_grid_colours @minor_grid_colours.setter def minor_grid_colours(self, value: ArrayLike) -> None: """Setter for the **self.minor_grid_colours** property.""" self._minor_grid_colours = value @visual_property def major_tick_labels(self) -> bool: """ Getter and setter property for the major tick labels state. Parameters ---------- value Value to set major tick labels state with. Returns ------- :class:`bool` Major tick labels state. """ return self._major_tick_labels @major_tick_labels.setter def major_tick_labels(self, value: bool) -> None: """Setter for the **self.major_tick_labels** property.""" self._major_tick_labels = value @visual_property def major_tick_label_colours(self) -> ArrayLike: """ Getter and setter property for the major tick label colour. Parameters ---------- value Value to set the major tick label colour with. Returns ------- ArrayLike Major tick label colour. """ return self._major_tick_label_colours @major_tick_label_colours.setter def major_tick_label_colours(self, value: ArrayLike) -> None: """Setter for the **self.major_tick_label_colours** property.""" self._major_tick_label_colours = value @visual_property def minor_tick_labels(self) -> bool: """ Getter and setter property for the minor tick labels state. Parameters ---------- value Value to set minor tick labels state with. Returns ------- :class:`bool` Minor tick labels state. """ return self._minor_tick_labels @minor_tick_labels.setter def minor_tick_labels(self, value: bool) -> None: """Setter for the **self.minor_tick_labels** property.""" self._minor_tick_labels = value @visual_property def minor_tick_label_colours(self) -> ArrayLike: """ Getter and setter property for the minor tick label colour. Parameters ---------- value Value to set the minor tick label colour with. Returns ------- ArrayLike Minor tick label colour. """ return self._minor_tick_label_colours @minor_tick_label_colours.setter def minor_tick_label_colours(self, value: ArrayLike) -> None: """Setter for the **self.minor_tick_label_colours** property.""" self._minor_tick_label_colours = value
[docs] def update(self) -> None: """Update the visual.""" if self._is_update_blocked: return self.clear() size = int(self._size) vertices, faces, outline = conform_primitive_dtype( primitive_grid( width_segments=size, height_segments=size, ) ) positions = vertices["position"] self._grid_major = gfx.Mesh( gfx.Geometry( positions=as_contiguous_array(positions), indices=np.reshape(outline[..., 1], (-1, 4)), colors=as_contiguous_array( append_channel( np.tile(self._major_grid_colours, (positions.shape[0], 1)), 1, ) ), ), gfx.MeshBasicMaterial(color_mode="vertex", wireframe=True), ) self._grid_major.local.scale = np.array([size, size, 1]) if not self._centred: self._grid_major.local.position = np.array([size / 2, size / 2, 0]) self.add(self._grid_major) vertices, faces, outline = conform_primitive_dtype( primitive_grid( width_segments=size * 10, height_segments=size * 10, ) ) positions = vertices["position"] self._grid_minor = gfx.Mesh( gfx.Geometry( positions=as_contiguous_array(positions), indices=np.reshape(outline[..., 1], (-1, 4)), colors=as_contiguous_array( append_channel( np.tile(self._minor_grid_colours, (positions.shape[0], 1)), 1, ) ), ), gfx.MeshBasicMaterial(color_mode="vertex", wireframe=True), ) self._grid_minor.local.scale = np.array([size, size, 1]) self._grid_minor.local.position = np.array([0, 0, -1e-3]) if not self._centred: self._grid_minor.local.position = np.array([size / 2, size / 2, -1e-3]) self.add(self._grid_minor) axes_positions = np.array( [ [0, 0, 0], [1, 0, 0], [0, 0, 0], [0, 1, 0], ], dtype=DEFAULT_FLOAT_DTYPE_WGPU, ) axes_positions *= size / 2 axes_colours = np.array( [ [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1], [0, 1, 0, 1], ], dtype=DEFAULT_FLOAT_DTYPE_WGPU, ) self._axes_helper = gfx.Line( gfx.Geometry(positions=axes_positions, colors=axes_colours), gfx.LineSegmentMaterial(color_mode="vertex", thickness=2), ) self.add(self._axes_helper) if self._major_tick_labels: self._ticks_major_x, self._ticks_major_y = [], [] start, end = -size / 2, size / 2 + 1 if not self._centred: start = 0 end = size + 1 for i in np.arange(start, end, 1): x_text = gfx.Text( gfx.TextGeometry( f"{i} " if i == 0 else str(i), font_size=CONSTANTS_COLOUR_STYLE.font.size, screen_space=True, anchor="Top-Right" if i == 0 else "Top-Center", ), gfx.TextMaterial(color=self._major_tick_label_colours), # pyright: ignore ) x_text.local.position = np.array([i, 0, 1e-3]) self.add(x_text) self._ticks_major_x.append(x_text) if i == 0: continue y_text = gfx.Text( gfx.TextGeometry( f"{i} ", font_size=CONSTANTS_COLOUR_STYLE.font.size, screen_space=True, anchor="Center-Right", ), gfx.TextMaterial(color=self._major_tick_label_colours), # pyright: ignore ) y_text.local.position = np.array([0, i, 1e-3]) self.add(y_text) self._ticks_major_y.append(y_text) if self._minor_tick_labels: self._ticks_minor_x, self._ticks_minor_y = [], [] start, end = -size / 2, size / 2 + 0.1 if not self._centred: start = 0 end = size + 0.1 for i in np.arange(start, end, 0.1): if np.around(i, 0) == np.around(i, 1): continue i = np.around(i, 1) # noqa: PLW2901 x_text = gfx.Text( gfx.TextGeometry( f"{i} " if i == 0 else str(i), font_size=CONSTANTS_COLOUR_STYLE.font.size * CONSTANTS_COLOUR_STYLE.font.scaling.small, screen_space=True, anchor="Top-Right" if i == 0 else "Top-Center", ), gfx.TextMaterial(color=self._minor_tick_label_colours), # pyright: ignore ) x_text.local.position = np.array([i, 0, 1e-3]) self.add(x_text) self._ticks_minor_x.append(x_text) if i == 0: continue y_text = gfx.Text( gfx.TextGeometry( f"{i} ", font_size=CONSTANTS_COLOUR_STYLE.font.size * CONSTANTS_COLOUR_STYLE.font.scaling.small, screen_space=True, anchor="Center-Right", ), gfx.TextMaterial(color=self._minor_tick_label_colours), # pyright: ignore ) y_text.local.position = np.array([0, i, 1e-3]) self.add(y_text) self._ticks_minor_y.append(y_text)
if __name__ == "__main__": scene = gfx.Scene() scene.add( gfx.Background(None, gfx.BackgroundMaterial(np.array([0.18, 0.18, 0.18]))) ) visual_1 = VisualGrid() scene.add(visual_1) gfx.show(scene, up=np.array([0, 0, 1]))