Skip to content

GSP API Reference

The Graphic Server Protocol (GSP) API provides a unified interface for creating and rendering graphics across different backends.

Core Module

gsp.core.canvas

Canvas module for the GSP library.

Canvas

Canvas class representing a drawing surface with specific dimensions and DPI.

Source code in src/gsp/core/canvas.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class Canvas:
    """Canvas class representing a drawing surface with specific dimensions and DPI."""

    __slots__ = ["_uuid", "_width", "_height", "_dpi", "userData"]

    def __init__(self, width: int, height: int, dpi: float):
        """Create a new Canvas object with the given width, height, and dpi.

        Args:
            width (int): Width of the canvas in pixels.
            height (int): Height of the canvas in pixels.
            dpi (float): Dots per inch (DPI) of the canvas. If set to the screen PPI, the 'inch' unit in will correspond to one physical inch on the screen.
        """
        self._uuid: str = UuidUtils.generate_uuid()
        self._width: int = width
        self._height: int = height
        self._dpi: float = dpi
        self.userData: dict[str, Any] = {}

    def __repr__(self) -> str:
        """Return string representation of the Canvas instance."""
        return f"Canvas(width={self._width}, height={self._height}, dpi={self._dpi})"

    def get_uuid(self) -> str:
        """Get the UUID of the Canvas instance.

        Returns:
            str: The UUID of the Canvas.
        """
        return self._uuid

    def get_width(self) -> int:
        """Get the width of the canvas in pixels."""
        return self._width

    def set_width(self, width: int) -> None:
        """Set the width of the canvas in pixels.

        Args:
            width (int): The new width in pixels.
        """
        self._width = width

    def get_height(self) -> int:
        """Get the height of the canvas in pixels."""
        return self._height

    def set_height(self, height: int) -> None:
        """Set the height of the canvas in pixels.

        Args:
            height (int): The new height in pixels.
        """
        self._height = height

    def get_dpi(self) -> float:
        """Get the DPI of the canvas."""
        return self._dpi

    def set_dpi(self, dpi: float) -> None:
        """Set the DPI of the canvas.

        Args:
            dpi (float): The new DPI value.
        """
        self._dpi = dpi

__init__(width: int, height: int, dpi: float)

Create a new Canvas object with the given width, height, and dpi.

Parameters:

Name Type Description Default
width int

Width of the canvas in pixels.

required
height int

Height of the canvas in pixels.

required
dpi float

Dots per inch (DPI) of the canvas. If set to the screen PPI, the 'inch' unit in will correspond to one physical inch on the screen.

required
Source code in src/gsp/core/canvas.py
14
15
16
17
18
19
20
21
22
23
24
25
26
def __init__(self, width: int, height: int, dpi: float):
    """Create a new Canvas object with the given width, height, and dpi.

    Args:
        width (int): Width of the canvas in pixels.
        height (int): Height of the canvas in pixels.
        dpi (float): Dots per inch (DPI) of the canvas. If set to the screen PPI, the 'inch' unit in will correspond to one physical inch on the screen.
    """
    self._uuid: str = UuidUtils.generate_uuid()
    self._width: int = width
    self._height: int = height
    self._dpi: float = dpi
    self.userData: dict[str, Any] = {}

__repr__() -> str

Return string representation of the Canvas instance.

Source code in src/gsp/core/canvas.py
28
29
30
def __repr__(self) -> str:
    """Return string representation of the Canvas instance."""
    return f"Canvas(width={self._width}, height={self._height}, dpi={self._dpi})"

get_dpi() -> float

Get the DPI of the canvas.

Source code in src/gsp/core/canvas.py
64
65
66
def get_dpi(self) -> float:
    """Get the DPI of the canvas."""
    return self._dpi

get_height() -> int

Get the height of the canvas in pixels.

Source code in src/gsp/core/canvas.py
52
53
54
def get_height(self) -> int:
    """Get the height of the canvas in pixels."""
    return self._height

get_uuid() -> str

Get the UUID of the Canvas instance.

Returns:

Name Type Description
str str

The UUID of the Canvas.

Source code in src/gsp/core/canvas.py
32
33
34
35
36
37
38
def get_uuid(self) -> str:
    """Get the UUID of the Canvas instance.

    Returns:
        str: The UUID of the Canvas.
    """
    return self._uuid

get_width() -> int

Get the width of the canvas in pixels.

Source code in src/gsp/core/canvas.py
40
41
42
def get_width(self) -> int:
    """Get the width of the canvas in pixels."""
    return self._width

set_dpi(dpi: float) -> None

Set the DPI of the canvas.

Parameters:

Name Type Description Default
dpi float

The new DPI value.

required
Source code in src/gsp/core/canvas.py
68
69
70
71
72
73
74
def set_dpi(self, dpi: float) -> None:
    """Set the DPI of the canvas.

    Args:
        dpi (float): The new DPI value.
    """
    self._dpi = dpi

set_height(height: int) -> None

Set the height of the canvas in pixels.

Parameters:

Name Type Description Default
height int

The new height in pixels.

required
Source code in src/gsp/core/canvas.py
56
57
58
59
60
61
62
def set_height(self, height: int) -> None:
    """Set the height of the canvas in pixels.

    Args:
        height (int): The new height in pixels.
    """
    self._height = height

set_width(width: int) -> None

Set the width of the canvas in pixels.

Parameters:

Name Type Description Default
width int

The new width in pixels.

required
Source code in src/gsp/core/canvas.py
44
45
46
47
48
49
50
def set_width(self, width: int) -> None:
    """Set the width of the canvas in pixels.

    Args:
        width (int): The new width in pixels.
    """
    self._width = width

gsp.core.viewport

Viewport class representing a rectangular area on the canvas.

Viewport

Viewport class representing a rectangular area on the canvas.

Source code in src/gsp/core/viewport.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
class Viewport:
    """Viewport class representing a rectangular area on the canvas."""

    __slots__ = ["_uuid", "_x", "_y", "_width", "_height", "userData"]

    def __init__(self, x: int, y: int, width: int, height: int):
        """Create a viewport.

        Args:
            x (int): The x offset (in pixels from left) of the viewport.
            y (int): The y offset (in pixels from bottom) of the viewport.
            width (int): The width (in pixels) of the viewport.
            height (int): The height (in pixels) of the viewport.
        """
        self._uuid: str = UuidUtils.generate_uuid()
        self._x: int = x
        self._y: int = y
        self._width: int = width
        self._height: int = height
        self.userData: dict[str, Any] = {}

    def __repr__(self) -> str:
        """Return string representation of the Viewport instance."""
        return f"Viewport(x={self._x}, y={self._y}, width={self._width}, height={self._height})"

    def get_uuid(self) -> str:
        """Get the unique identifier of the viewport.

        Returns:
            str: The unique identifier.
        """
        return self._uuid

    def get_x(self) -> int:
        """Get the x offset (in pixels from left) of the viewport.

        Returns:
            int: The x offset.
        """
        return self._x

    def set_x(self, x: int) -> None:
        """Set the x offset (in pixels from left) of the viewport.

        Args:
            x (int): The new x offset.
        """
        self._x = x

    def get_y(self) -> int:
        """Get the y offset (in pixels from bottom) of the viewport.

        Returns:
            int: The y offset.
        """
        return self._y

    def set_y(self, y: int) -> None:
        """Set the y offset (in pixels from bottom) of the viewport.

        Args:
            y (int): The new y offset.
        """
        self._y = y

    def get_width(self) -> int:
        """Get the width (in pixels) of the viewport.

        Returns:
            int: The width.
        """
        return self._width

    def set_width(self, width: int) -> None:
        """Set the width (in pixels) of the viewport.

        Args:
            width (int): The new width.
        """
        self._width = width

    def get_height(self) -> int:
        """Get the height (in pixels) of the viewport.

        Returns:
            int: The height.
        """
        return self._height

    def set_height(self, height: int) -> None:
        """Set the height (in pixels) of the viewport.

        Args:
            height (int): The new height.
        """
        self._height = height

__init__(x: int, y: int, width: int, height: int)

Create a viewport.

Parameters:

Name Type Description Default
x int

The x offset (in pixels from left) of the viewport.

required
y int

The y offset (in pixels from bottom) of the viewport.

required
width int

The width (in pixels) of the viewport.

required
height int

The height (in pixels) of the viewport.

required
Source code in src/gsp/core/viewport.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def __init__(self, x: int, y: int, width: int, height: int):
    """Create a viewport.

    Args:
        x (int): The x offset (in pixels from left) of the viewport.
        y (int): The y offset (in pixels from bottom) of the viewport.
        width (int): The width (in pixels) of the viewport.
        height (int): The height (in pixels) of the viewport.
    """
    self._uuid: str = UuidUtils.generate_uuid()
    self._x: int = x
    self._y: int = y
    self._width: int = width
    self._height: int = height
    self.userData: dict[str, Any] = {}

__repr__() -> str

Return string representation of the Viewport instance.

Source code in src/gsp/core/viewport.py
31
32
33
def __repr__(self) -> str:
    """Return string representation of the Viewport instance."""
    return f"Viewport(x={self._x}, y={self._y}, width={self._width}, height={self._height})"

get_height() -> int

Get the height (in pixels) of the viewport.

Returns:

Name Type Description
int int

The height.

Source code in src/gsp/core/viewport.py
91
92
93
94
95
96
97
def get_height(self) -> int:
    """Get the height (in pixels) of the viewport.

    Returns:
        int: The height.
    """
    return self._height

get_uuid() -> str

Get the unique identifier of the viewport.

Returns:

Name Type Description
str str

The unique identifier.

Source code in src/gsp/core/viewport.py
35
36
37
38
39
40
41
def get_uuid(self) -> str:
    """Get the unique identifier of the viewport.

    Returns:
        str: The unique identifier.
    """
    return self._uuid

get_width() -> int

Get the width (in pixels) of the viewport.

Returns:

Name Type Description
int int

The width.

Source code in src/gsp/core/viewport.py
75
76
77
78
79
80
81
def get_width(self) -> int:
    """Get the width (in pixels) of the viewport.

    Returns:
        int: The width.
    """
    return self._width

get_x() -> int

Get the x offset (in pixels from left) of the viewport.

Returns:

Name Type Description
int int

The x offset.

Source code in src/gsp/core/viewport.py
43
44
45
46
47
48
49
def get_x(self) -> int:
    """Get the x offset (in pixels from left) of the viewport.

    Returns:
        int: The x offset.
    """
    return self._x

get_y() -> int

Get the y offset (in pixels from bottom) of the viewport.

Returns:

Name Type Description
int int

The y offset.

Source code in src/gsp/core/viewport.py
59
60
61
62
63
64
65
def get_y(self) -> int:
    """Get the y offset (in pixels from bottom) of the viewport.

    Returns:
        int: The y offset.
    """
    return self._y

set_height(height: int) -> None

Set the height (in pixels) of the viewport.

Parameters:

Name Type Description Default
height int

The new height.

required
Source code in src/gsp/core/viewport.py
 99
100
101
102
103
104
105
def set_height(self, height: int) -> None:
    """Set the height (in pixels) of the viewport.

    Args:
        height (int): The new height.
    """
    self._height = height

set_width(width: int) -> None

Set the width (in pixels) of the viewport.

Parameters:

Name Type Description Default
width int

The new width.

required
Source code in src/gsp/core/viewport.py
83
84
85
86
87
88
89
def set_width(self, width: int) -> None:
    """Set the width (in pixels) of the viewport.

    Args:
        width (int): The new width.
    """
    self._width = width

set_x(x: int) -> None

Set the x offset (in pixels from left) of the viewport.

Parameters:

Name Type Description Default
x int

The new x offset.

required
Source code in src/gsp/core/viewport.py
51
52
53
54
55
56
57
def set_x(self, x: int) -> None:
    """Set the x offset (in pixels from left) of the viewport.

    Args:
        x (int): The new x offset.
    """
    self._x = x

set_y(y: int) -> None

Set the y offset (in pixels from bottom) of the viewport.

Parameters:

Name Type Description Default
y int

The new y offset.

required
Source code in src/gsp/core/viewport.py
67
68
69
70
71
72
73
def set_y(self, y: int) -> None:
    """Set the y offset (in pixels from bottom) of the viewport.

    Args:
        y (int): The new y offset.
    """
    self._y = y

gsp.core.camera

Camera module for the GSP library.

Camera

Camera class representing a view and projection matrix for 3D rendering.

Source code in src/gsp/core/camera.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Camera:
    """Camera class representing a view and projection matrix for 3D rendering."""
    __slots__ = ["_uuid", "_view_matrix", "_projection_matrix", "userData"]

    def __init__(self, view_matrix: TransBuf, projection_matrix: TransBuf):
        """Initialize a Camera instance. Just a container for view and projection matrices.

        Args:
            view_matrix (TransBuf): View matrix - [view-matrix](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#the-view-matrix)
            projection_matrix (TransBuf): Projection matrix - [projection-matrix](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#the-projection-matrix)
        """
        self._uuid: str = UuidUtils.generate_uuid()
        self._view_matrix: TransBuf = view_matrix
        self._projection_matrix: TransBuf = projection_matrix
        self.userData: dict[str, Any] = {}
        """A dictionary for storing custom user data associated with the Camera instance."""

    def __repr__(self) -> str:
        """Return string representation of the Camera instance."""
        return f"Camera(uuid={self._uuid})"

    def get_uuid(self) -> str:
        """Get the UUID of the Camera instance.

        Returns:
            str: The UUID of the Camera.
        """
        return self._uuid

    def set_view_matrix(self, view_matrix: TransBuf):
        """Set the view matrix of the Camera.

        Args:
            view_matrix (TransBuf): The new view matrix.
        """
        self._view_matrix = view_matrix

    def get_view_matrix(self) -> TransBuf:
        """Get the view matrix of the Camera.

        Returns:
            TransBuf: The view matrix.
        """
        return self._view_matrix

    def set_projection_matrix(self, projection_matrix: TransBuf):
        """Set the projection matrix of the Camera.

        Args:
            projection_matrix (TransBuf): The new projection matrix.
        """
        self._projection_matrix = projection_matrix

    def get_projection_matrix(self) -> TransBuf:
        """Get the projection matrix of the Camera.

        Returns:
            TransBuf: The projection matrix.
        """
        return self._projection_matrix

userData: dict[str, Any] = {} instance-attribute

A dictionary for storing custom user data associated with the Camera instance.

__init__(view_matrix: TransBuf, projection_matrix: TransBuf)

Initialize a Camera instance. Just a container for view and projection matrices.

Parameters:

Name Type Description Default
view_matrix gsp.types.transbuf.TransBuf

View matrix - view-matrix

required
projection_matrix gsp.types.transbuf.TransBuf

Projection matrix - projection-matrix

required
Source code in src/gsp/core/camera.py
14
15
16
17
18
19
20
21
22
23
24
25
def __init__(self, view_matrix: TransBuf, projection_matrix: TransBuf):
    """Initialize a Camera instance. Just a container for view and projection matrices.

    Args:
        view_matrix (TransBuf): View matrix - [view-matrix](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#the-view-matrix)
        projection_matrix (TransBuf): Projection matrix - [projection-matrix](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#the-projection-matrix)
    """
    self._uuid: str = UuidUtils.generate_uuid()
    self._view_matrix: TransBuf = view_matrix
    self._projection_matrix: TransBuf = projection_matrix
    self.userData: dict[str, Any] = {}
    """A dictionary for storing custom user data associated with the Camera instance."""

__repr__() -> str

Return string representation of the Camera instance.

Source code in src/gsp/core/camera.py
27
28
29
def __repr__(self) -> str:
    """Return string representation of the Camera instance."""
    return f"Camera(uuid={self._uuid})"

get_projection_matrix() -> TransBuf

Get the projection matrix of the Camera.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The projection matrix.

Source code in src/gsp/core/camera.py
63
64
65
66
67
68
69
def get_projection_matrix(self) -> TransBuf:
    """Get the projection matrix of the Camera.

    Returns:
        TransBuf: The projection matrix.
    """
    return self._projection_matrix

get_uuid() -> str

Get the UUID of the Camera instance.

Returns:

Name Type Description
str str

The UUID of the Camera.

Source code in src/gsp/core/camera.py
31
32
33
34
35
36
37
def get_uuid(self) -> str:
    """Get the UUID of the Camera instance.

    Returns:
        str: The UUID of the Camera.
    """
    return self._uuid

get_view_matrix() -> TransBuf

Get the view matrix of the Camera.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The view matrix.

Source code in src/gsp/core/camera.py
47
48
49
50
51
52
53
def get_view_matrix(self) -> TransBuf:
    """Get the view matrix of the Camera.

    Returns:
        TransBuf: The view matrix.
    """
    return self._view_matrix

set_projection_matrix(projection_matrix: TransBuf)

Set the projection matrix of the Camera.

Parameters:

Name Type Description Default
projection_matrix gsp.types.transbuf.TransBuf

The new projection matrix.

required
Source code in src/gsp/core/camera.py
55
56
57
58
59
60
61
def set_projection_matrix(self, projection_matrix: TransBuf):
    """Set the projection matrix of the Camera.

    Args:
        projection_matrix (TransBuf): The new projection matrix.
    """
    self._projection_matrix = projection_matrix

set_view_matrix(view_matrix: TransBuf)

Set the view matrix of the Camera.

Parameters:

Name Type Description Default
view_matrix gsp.types.transbuf.TransBuf

The new view matrix.

required
Source code in src/gsp/core/camera.py
39
40
41
42
43
44
45
def set_view_matrix(self, view_matrix: TransBuf):
    """Set the view matrix of the Camera.

    Args:
        view_matrix (TransBuf): The new view matrix.
    """
    self._view_matrix = view_matrix

gsp.core.event

A simple, standalone event implementation with subscribe and dispatch methods.

Event

Bases: typing.Generic[gsp.core.event.Callback]

A simple, standalone event implementation with subscribe and dispatch methods.

This class allows for a "one-to-many" communication pattern where a single event can notify multiple listeners (subscribers) that an action has occurred.

The generic type Callback allows the event to be type-hinted with the specific signature of the functions it will dispatch to, ensuring type safety.

Source code in src/gsp/core/event.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class Event(Generic[Callback]):
    """A simple, standalone event implementation with subscribe and dispatch methods.

    This class allows for a "one-to-many" communication pattern where a single event
    can notify multiple listeners (subscribers) that an action has occurred.

    The generic type `Callback` allows the event to be type-hinted with the
    specific signature of the functions it will dispatch to, ensuring type safety.
    """
    def __init__(self):
        """Initialize the Event with an empty list of callbacks."""
        # A list to store the subscribed callback functions.
        self._callbacks: list[Callback] = []

    def subscribe(self, callback: Callback) -> None:
        """Subscribes a callback to the event.

        Args:
            callback (Callback): The function to be called when the event is dispatched.
                      Its signature should match the event's generic type.
        """
        self._callbacks.append(callback)

    def unsubscribe(self, callback: Callback) -> None:
        """Unsubscribes a previously subscribed callback from the event.

        Args:
            callback (Callback): The function to be removed from the event's subscribers.
        """
        self._callbacks.remove(callback)

    def dispatch(self, *args: Any, **kwargs: Any) -> None:
        """Dispatches the event, calling all subscribed callbacks with the given arguments.

        Args:
            *args (Any): Positional arguments to pass to the callbacks.
            **kwargs (Any): Keyword arguments to pass to the callbacks.
        """
        for callback in self._callbacks:
            callback(*args, **kwargs)

    def event_listener(self, callback: Callback) -> Callback:
        """Decorator to subscribe a function to the event. This is a convenience method.

        It still ensure static type checking on the decorated function.
        **NOTE**: it is possible to unsubscribe the function later using `event.unsubscribe(handler)`.

        Usage:
            @event.subscriber
            def handler(...): ...

        Args:
            callback (Callback): The function to be subscribed.

        Returns:
            Callback: The same function that was passed in.
        """
        self.subscribe(callback)
        return callback

__init__()

Initialize the Event with an empty list of callbacks.

Source code in src/gsp/core/event.py
19
20
21
22
def __init__(self):
    """Initialize the Event with an empty list of callbacks."""
    # A list to store the subscribed callback functions.
    self._callbacks: list[Callback] = []

dispatch(*args: Any, **kwargs: Any) -> None

Dispatches the event, calling all subscribed callbacks with the given arguments.

Parameters:

Name Type Description Default
*args typing.Any

Positional arguments to pass to the callbacks.

()
**kwargs typing.Any

Keyword arguments to pass to the callbacks.

{}
Source code in src/gsp/core/event.py
41
42
43
44
45
46
47
48
49
def dispatch(self, *args: Any, **kwargs: Any) -> None:
    """Dispatches the event, calling all subscribed callbacks with the given arguments.

    Args:
        *args (Any): Positional arguments to pass to the callbacks.
        **kwargs (Any): Keyword arguments to pass to the callbacks.
    """
    for callback in self._callbacks:
        callback(*args, **kwargs)

event_listener(callback: Callback) -> Callback

Decorator to subscribe a function to the event. This is a convenience method.

It still ensure static type checking on the decorated function. NOTE: it is possible to unsubscribe the function later using event.unsubscribe(handler).

Usage

@event.subscriber def handler(...): ...

Parameters:

Name Type Description Default
callback gsp.core.event.Callback

The function to be subscribed.

required

Returns:

Name Type Description
Callback gsp.core.event.Callback

The same function that was passed in.

Source code in src/gsp/core/event.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def event_listener(self, callback: Callback) -> Callback:
    """Decorator to subscribe a function to the event. This is a convenience method.

    It still ensure static type checking on the decorated function.
    **NOTE**: it is possible to unsubscribe the function later using `event.unsubscribe(handler)`.

    Usage:
        @event.subscriber
        def handler(...): ...

    Args:
        callback (Callback): The function to be subscribed.

    Returns:
        Callback: The same function that was passed in.
    """
    self.subscribe(callback)
    return callback

subscribe(callback: Callback) -> None

Subscribes a callback to the event.

Parameters:

Name Type Description Default
callback gsp.core.event.Callback

The function to be called when the event is dispatched. Its signature should match the event's generic type.

required
Source code in src/gsp/core/event.py
24
25
26
27
28
29
30
31
def subscribe(self, callback: Callback) -> None:
    """Subscribes a callback to the event.

    Args:
        callback (Callback): The function to be called when the event is dispatched.
                  Its signature should match the event's generic type.
    """
    self._callbacks.append(callback)

unsubscribe(callback: Callback) -> None

Unsubscribes a previously subscribed callback from the event.

Parameters:

Name Type Description Default
callback gsp.core.event.Callback

The function to be removed from the event's subscribers.

required
Source code in src/gsp/core/event.py
33
34
35
36
37
38
39
def unsubscribe(self, callback: Callback) -> None:
    """Unsubscribes a previously subscribed callback from the event.

    Args:
        callback (Callback): The function to be removed from the event's subscribers.
    """
    self._callbacks.remove(callback)

main() -> None

Example usage of the Event class.

Source code in src/gsp/core/event.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def main() -> None:
    """Example usage of the Event class."""
    from typing import Protocol

    # We can define the expected function signature using a Protocol for clarity.
    class UserLoginCallback(Protocol):
        def __call__(self, username: str, user_id: int) -> None: ...

    # Create an instance of our Event, typed with the protocol.
    on_user_login = Event[UserLoginCallback]()

    def welcome_user(username: str, user_id: int) -> None:
        print(f"Welcome, {username}! Your user ID is {user_id}.")

    def log_login_event(username: str, user_id: float) -> None:
        print(f"[{username}] logged in with ID [{user_id}] at the database level.")

    # Subscribe the functions
    on_user_login.subscribe(welcome_user)
    on_user_login.subscribe(log_login_event)

    # Dispatch the event
    print("Dispatching event for user 'Alice'...")
    on_user_login.dispatch("Alice", "ddd")

Types Module

gsp.types.buffer

Buffer module for typed array with single dimension.

Buffer

Typed array with single dimension.

It is immutable in count and type, but mutable in content.

Source code in src/gsp/types/buffer.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class Buffer:
    """Typed array with single dimension.

    It is immutable in count and type, but mutable in content.
    """

    def __init__(self, count: int, buffer_type: BufferType) -> None:
        """Initialize a Buffer instance.

        Args:
            count (int): The number of elements in the buffer.
            buffer_type (BufferType): The type of elements in the buffer.
        """
        item_size = BufferType.get_item_size(buffer_type)
        self._count: int = count
        self._type: BufferType = buffer_type
        self._bytearray: bytearray = bytearray(count * item_size)

    def __repr__(self) -> str:
        """Return a string representation of the Buffer.

        Returns:
            str: A string representation showing count and type.
        """
        return f"Buffer(count={self._count}, type={self._type})"

    def get_count(self) -> int:
        """Return the number of elements in the buffer.

        Returns:
            int: The number of elements.
        """
        return self._count

    def get_type(self) -> BufferType:
        """Return the type of each element in the buffer.

        Returns:
            BufferType: The buffer type.
        """
        return self._type

    # =============================================================================
    # .get_data/.set_data
    # =============================================================================

    def get_data(self, offset: int, count: int) -> "Buffer":
        """Return a buffer of count elements starting from offset.

        Args:
            offset (int): The starting index.
            count (int): The number of elements to retrieve.

        Returns:
            Buffer: A new Buffer containing the requested data.
        """
        item_size = BufferType.get_item_size(self._type)
        start = offset * item_size
        end = start + count * item_size

        new_buffer = Buffer(count, self._type)
        new_buffer.set_data(self._bytearray[start:end], 0, count)
        return new_buffer

    def set_data(self, _bytearray: bytearray, offset: int, count: int) -> None:
        """Copy count elements starting from offset in the source bytearray.

        Args:
            _bytearray (bytearray): The source bytearray containing data to copy.
            offset (int): The starting index in the buffer where data will be copied.
            count (int): The number of elements to copy.
        """
        item_size = BufferType.get_item_size(self._type)

        # sanity check
        assert offset + count <= self._count, f"Invalid offset {offset} and count {count} for buffer of size {self._count}"

        start = offset * item_size
        end = start + count * item_size
        self._bytearray = self._bytearray[:start] + _bytearray[0 : count * item_size] + self._bytearray[end:]

    # =============================================================================
    # .to_bytearray/from_bytearray
    # =============================================================================

    def to_bytearray(self) -> bytearray:
        """Return the content of the Buffer as a bytearray.

        Returns:
            bytearray: The bytearray representation of the Buffer.
        """
        return bytearray(self._bytearray)

    @staticmethod
    def from_bytearray(_bytearray: bytearray, buffer_type: BufferType) -> "Buffer":
        """Create a Buffer from a bytearray and a specified BufferType.

        Args:
            _bytearray (bytearray): The source bytearray.
            buffer_type (BufferType): The type of elements in the buffer.

        Returns:
            Buffer: The created Buffer instance.
        """
        item_size = BufferType.get_item_size(buffer_type)
        # sanity check
        assert len(_bytearray) % item_size == 0, f"data size {len(_bytearray)} is not aligned with buffer type item size {item_size}"

        # create buffer
        buffer = Buffer(len(_bytearray) // item_size, buffer_type)
        buffer.set_data(_bytearray, 0, buffer.get_count())
        return buffer

__init__(count: int, buffer_type: BufferType) -> None

Initialize a Buffer instance.

Parameters:

Name Type Description Default
count int

The number of elements in the buffer.

required
buffer_type gsp.types.buffer_type.BufferType

The type of elements in the buffer.

required
Source code in src/gsp/types/buffer.py
19
20
21
22
23
24
25
26
27
28
29
def __init__(self, count: int, buffer_type: BufferType) -> None:
    """Initialize a Buffer instance.

    Args:
        count (int): The number of elements in the buffer.
        buffer_type (BufferType): The type of elements in the buffer.
    """
    item_size = BufferType.get_item_size(buffer_type)
    self._count: int = count
    self._type: BufferType = buffer_type
    self._bytearray: bytearray = bytearray(count * item_size)

__repr__() -> str

Return a string representation of the Buffer.

Returns:

Name Type Description
str str

A string representation showing count and type.

Source code in src/gsp/types/buffer.py
31
32
33
34
35
36
37
def __repr__(self) -> str:
    """Return a string representation of the Buffer.

    Returns:
        str: A string representation showing count and type.
    """
    return f"Buffer(count={self._count}, type={self._type})"

from_bytearray(_bytearray: bytearray, buffer_type: BufferType) -> Buffer staticmethod

Create a Buffer from a bytearray and a specified BufferType.

Parameters:

Name Type Description Default
_bytearray bytearray

The source bytearray.

required
buffer_type gsp.types.buffer_type.BufferType

The type of elements in the buffer.

required

Returns:

Name Type Description
Buffer gsp.types.buffer.Buffer

The created Buffer instance.

Source code in src/gsp/types/buffer.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
@staticmethod
def from_bytearray(_bytearray: bytearray, buffer_type: BufferType) -> "Buffer":
    """Create a Buffer from a bytearray and a specified BufferType.

    Args:
        _bytearray (bytearray): The source bytearray.
        buffer_type (BufferType): The type of elements in the buffer.

    Returns:
        Buffer: The created Buffer instance.
    """
    item_size = BufferType.get_item_size(buffer_type)
    # sanity check
    assert len(_bytearray) % item_size == 0, f"data size {len(_bytearray)} is not aligned with buffer type item size {item_size}"

    # create buffer
    buffer = Buffer(len(_bytearray) // item_size, buffer_type)
    buffer.set_data(_bytearray, 0, buffer.get_count())
    return buffer

get_count() -> int

Return the number of elements in the buffer.

Returns:

Name Type Description
int int

The number of elements.

Source code in src/gsp/types/buffer.py
39
40
41
42
43
44
45
def get_count(self) -> int:
    """Return the number of elements in the buffer.

    Returns:
        int: The number of elements.
    """
    return self._count

get_data(offset: int, count: int) -> Buffer

Return a buffer of count elements starting from offset.

Parameters:

Name Type Description Default
offset int

The starting index.

required
count int

The number of elements to retrieve.

required

Returns:

Name Type Description
Buffer gsp.types.buffer.Buffer

A new Buffer containing the requested data.

Source code in src/gsp/types/buffer.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def get_data(self, offset: int, count: int) -> "Buffer":
    """Return a buffer of count elements starting from offset.

    Args:
        offset (int): The starting index.
        count (int): The number of elements to retrieve.

    Returns:
        Buffer: A new Buffer containing the requested data.
    """
    item_size = BufferType.get_item_size(self._type)
    start = offset * item_size
    end = start + count * item_size

    new_buffer = Buffer(count, self._type)
    new_buffer.set_data(self._bytearray[start:end], 0, count)
    return new_buffer

get_type() -> BufferType

Return the type of each element in the buffer.

Returns:

Name Type Description
BufferType gsp.types.buffer_type.BufferType

The buffer type.

Source code in src/gsp/types/buffer.py
47
48
49
50
51
52
53
def get_type(self) -> BufferType:
    """Return the type of each element in the buffer.

    Returns:
        BufferType: The buffer type.
    """
    return self._type

set_data(_bytearray: bytearray, offset: int, count: int) -> None

Copy count elements starting from offset in the source bytearray.

Parameters:

Name Type Description Default
_bytearray bytearray

The source bytearray containing data to copy.

required
offset int

The starting index in the buffer where data will be copied.

required
count int

The number of elements to copy.

required
Source code in src/gsp/types/buffer.py
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def set_data(self, _bytearray: bytearray, offset: int, count: int) -> None:
    """Copy count elements starting from offset in the source bytearray.

    Args:
        _bytearray (bytearray): The source bytearray containing data to copy.
        offset (int): The starting index in the buffer where data will be copied.
        count (int): The number of elements to copy.
    """
    item_size = BufferType.get_item_size(self._type)

    # sanity check
    assert offset + count <= self._count, f"Invalid offset {offset} and count {count} for buffer of size {self._count}"

    start = offset * item_size
    end = start + count * item_size
    self._bytearray = self._bytearray[:start] + _bytearray[0 : count * item_size] + self._bytearray[end:]

to_bytearray() -> bytearray

Return the content of the Buffer as a bytearray.

Returns:

Name Type Description
bytearray bytearray

The bytearray representation of the Buffer.

Source code in src/gsp/types/buffer.py
 98
 99
100
101
102
103
104
def to_bytearray(self) -> bytearray:
    """Return the content of the Buffer as a bytearray.

    Returns:
        bytearray: The bytearray representation of the Buffer.
    """
    return bytearray(self._bytearray)

gsp.types.group

Group type definitions for GSP.

This module defines types used to represent groups of elements in various forms.

Groups = Union[int, list[int], list[list[int]]] module-attribute

A type that can represent group IDs in various forms.

The Groups type supports three different formats
  • int: Represents the size of a single group.
  • list[int]: Each int represents the size of each subgroup.
  • list[list[int]]: Each sublist represents element indices in a group.
    • len(groups) represents the number of groups.
    • groups[0] contains element indices of the first group.
    • groups[1] contains element indices of the second group.

gsp.types.visual_base

Base class for visual objects in GSP.

This module provides the foundational VisualBase class that all visual objects inherit from, providing common functionality like UUID generation and user data storage.

VisualBase

Base class for all visual objects in the GSP library.

This class provides fundamental functionality for visual objects including automatic UUID generation and a userData dictionary for storing custom metadata.

Source code in src/gsp/types/visual_base.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class VisualBase:
    """Base class for all visual objects in the GSP library.

    This class provides fundamental functionality for visual objects including
    automatic UUID generation and a userData dictionary for storing custom
    metadata.
    """

    __slots__ = ["_uuid", "userData"]

    def __init__(self):
        """Initialize a new VisualBase instance.

        Creates a new visual object with a unique identifier and an empty
        userData dictionary.
        """
        self._uuid: str = UuidUtils.generate_uuid()
        self.userData: dict[str, Any] = {}

    def get_uuid(self) -> str:
        """Get the unique identifier of the visual object.

        Returns:
            str: The unique identifier.
        """
        return self._uuid

    def set_uuid(self, uuid: str) -> None:
        """Set the unique identifier of the visual object.

        Args:
            uuid: The new unique identifier.
        """
        self._uuid = uuid

__init__()

Initialize a new VisualBase instance.

Creates a new visual object with a unique identifier and an empty userData dictionary.

Source code in src/gsp/types/visual_base.py
25
26
27
28
29
30
31
32
def __init__(self):
    """Initialize a new VisualBase instance.

    Creates a new visual object with a unique identifier and an empty
    userData dictionary.
    """
    self._uuid: str = UuidUtils.generate_uuid()
    self.userData: dict[str, Any] = {}

get_uuid() -> str

Get the unique identifier of the visual object.

Returns:

Name Type Description
str str

The unique identifier.

Source code in src/gsp/types/visual_base.py
34
35
36
37
38
39
40
def get_uuid(self) -> str:
    """Get the unique identifier of the visual object.

    Returns:
        str: The unique identifier.
    """
    return self._uuid

set_uuid(uuid: str) -> None

Set the unique identifier of the visual object.

Parameters:

Name Type Description Default
uuid str

The new unique identifier.

required
Source code in src/gsp/types/visual_base.py
42
43
44
45
46
47
48
def set_uuid(self, uuid: str) -> None:
    """Set the unique identifier of the visual object.

    Args:
        uuid: The new unique identifier.
    """
    self._uuid = uuid

Visuals Module

gsp.visuals.points

Points visual module.

Points

Bases: gsp.types.visual_base.VisualBase

Points visual for rendering point markers.

This visual represents a collection of points with configurable positions, sizes, face colors, edge colors, and edge widths.

Source code in src/gsp/visuals/points.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
class Points(VisualBase):
    """Points visual for rendering point markers.

    This visual represents a collection of points with configurable positions,
    sizes, face colors, edge colors, and edge widths.
    """
    __slots__ = ["_positions", "_sizes", "_face_colors", "_edge_colors", "_edge_widths"]

    def __init__(self, positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf):
        """Initialize Points visual.

        Args:
            positions: Positions of the points.
            sizes: Sizes of the points.
            face_colors: Face colors of the points.
            edge_colors: Edge colors of the points.
            edge_widths: Edge widths of the points.
        """
        super().__init__()

        self._positions: TransBuf = positions
        self._sizes: TransBuf = sizes
        self._face_colors: TransBuf = face_colors
        self._edge_colors: TransBuf = edge_colors
        self._edge_widths: TransBuf = edge_widths

        self.check_attributes()

    # =============================================================================
    # get/set attributes
    # =============================================================================

    def get_positions(self) -> TransBuf:
        """Get positions of the points."""
        return self._positions

    def set_positions(self, positions: TransBuf) -> None:
        """Set positions of the points.

        Args:
            positions: New positions for the points.
        """
        self._positions = positions
        self.check_attributes()

    def get_sizes(self) -> TransBuf:
        """Get sizes of the points."""
        return self._sizes

    def set_sizes(self, sizes: TransBuf) -> None:
        """Set sizes of the points.

        Args:
            sizes: New sizes for the points.
        """
        self._sizes = sizes
        self.check_attributes()

    def get_face_colors(self) -> TransBuf:
        """Get face colors of the points."""
        return self._face_colors

    def set_face_colors(self, face_colors: TransBuf) -> None:
        """Set face colors of the points.

        Args:
            face_colors: New face colors for the points.
        """
        self._face_colors = face_colors
        self.check_attributes()

    def get_edge_colors(self) -> TransBuf:
        """Get edge colors of the points."""
        return self._edge_colors

    def set_edge_colors(self, edge_colors: TransBuf) -> None:
        """Set edge colors of the points.

        Args:
            edge_colors: New edge colors for the points.
        """
        self._edge_colors = edge_colors
        self.check_attributes()

    def get_edge_widths(self) -> TransBuf:
        """Get edge widths of the points."""
        return self._edge_widths

    def set_edge_widths(self, edge_widths: TransBuf) -> None:
        """Set edge widths of the points.

        Args:
            edge_widths: New edge widths for the points.
        """
        self._edge_widths = edge_widths
        self.check_attributes()

    def set_attributes(
        self,
        positions: TransBuf | None = None,
        sizes: TransBuf | None = None,
        face_colors: TransBuf | None = None,
        edge_colors: TransBuf | None = None,
        edge_widths: TransBuf | None = None,
    ) -> None:
        """Set multiple attributes at once and then check their validity."""
        if positions is not None:
            self._positions = positions
        if sizes is not None:
            self._sizes = sizes
        if face_colors is not None:
            self._face_colors = face_colors
        if edge_colors is not None:
            self._edge_colors = edge_colors
        if edge_widths is not None:
            self._edge_widths = edge_widths

        self.check_attributes()

    # =============================================================================
    # Sanity check functions
    # =============================================================================

    def check_attributes(self) -> None:
        """Check that the attributes are valid and consistent."""
        self.sanity_check_attributes(self._positions, self._sizes, self._face_colors, self._edge_colors, self._edge_widths)

    @staticmethod
    def sanity_check_attributes_buffer(positions: Buffer, sizes: Buffer, face_colors: Buffer, edge_colors: Buffer, edge_widths: Buffer):
        """Same as .sanity_check_attributes() but accept only Buffers.

        - It is meant to be used after converting TransBuf to Buffer.
        """
        # sanity check - each attribute must be a Buffer (not a transform chain)
        assert isinstance(positions, Buffer), "Positions must be a Buffer"
        assert isinstance(sizes, Buffer), "Sizes must be a Buffer"
        assert isinstance(face_colors, Buffer), "Face colors must be a Buffer"
        assert isinstance(edge_colors, Buffer), "Edge colors must be a Buffer"
        assert isinstance(edge_widths, Buffer), "Edge widths must be a Buffer"

        Points.sanity_check_attributes(positions, sizes, face_colors, edge_colors, edge_widths)

    @staticmethod
    def sanity_check_attributes(
        positions: TransBuf,
        sizes: TransBuf,
        face_colors: TransBuf,
        edge_colors: TransBuf,
        edge_widths: TransBuf,
    ) -> None:
        """Check that the attributes are valid and consistent.

        Args:
            positions: Positions of the points.
            sizes: Sizes of the points.
            face_colors: Face colors of the points.
            edge_colors: Edge colors of the points.
            edge_widths: Edge widths of the points.
        """
        pass

__init__(positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf)

Initialize Points visual.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the points.

required
sizes gsp.types.transbuf.TransBuf

Sizes of the points.

required
face_colors gsp.types.transbuf.TransBuf

Face colors of the points.

required
edge_colors gsp.types.transbuf.TransBuf

Edge colors of the points.

required
edge_widths gsp.types.transbuf.TransBuf

Edge widths of the points.

required
Source code in src/gsp/visuals/points.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def __init__(self, positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf):
    """Initialize Points visual.

    Args:
        positions: Positions of the points.
        sizes: Sizes of the points.
        face_colors: Face colors of the points.
        edge_colors: Edge colors of the points.
        edge_widths: Edge widths of the points.
    """
    super().__init__()

    self._positions: TransBuf = positions
    self._sizes: TransBuf = sizes
    self._face_colors: TransBuf = face_colors
    self._edge_colors: TransBuf = edge_colors
    self._edge_widths: TransBuf = edge_widths

    self.check_attributes()

check_attributes() -> None

Check that the attributes are valid and consistent.

Source code in src/gsp/visuals/points.py
132
133
134
def check_attributes(self) -> None:
    """Check that the attributes are valid and consistent."""
    self.sanity_check_attributes(self._positions, self._sizes, self._face_colors, self._edge_colors, self._edge_widths)

get_edge_colors() -> TransBuf

Get edge colors of the points.

Source code in src/gsp/visuals/points.py
80
81
82
def get_edge_colors(self) -> TransBuf:
    """Get edge colors of the points."""
    return self._edge_colors

get_edge_widths() -> TransBuf

Get edge widths of the points.

Source code in src/gsp/visuals/points.py
93
94
95
def get_edge_widths(self) -> TransBuf:
    """Get edge widths of the points."""
    return self._edge_widths

get_face_colors() -> TransBuf

Get face colors of the points.

Source code in src/gsp/visuals/points.py
67
68
69
def get_face_colors(self) -> TransBuf:
    """Get face colors of the points."""
    return self._face_colors

get_positions() -> TransBuf

Get positions of the points.

Source code in src/gsp/visuals/points.py
41
42
43
def get_positions(self) -> TransBuf:
    """Get positions of the points."""
    return self._positions

get_sizes() -> TransBuf

Get sizes of the points.

Source code in src/gsp/visuals/points.py
54
55
56
def get_sizes(self) -> TransBuf:
    """Get sizes of the points."""
    return self._sizes

sanity_check_attributes(positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf) -> None staticmethod

Check that the attributes are valid and consistent.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the points.

required
sizes gsp.types.transbuf.TransBuf

Sizes of the points.

required
face_colors gsp.types.transbuf.TransBuf

Face colors of the points.

required
edge_colors gsp.types.transbuf.TransBuf

Edge colors of the points.

required
edge_widths gsp.types.transbuf.TransBuf

Edge widths of the points.

required
Source code in src/gsp/visuals/points.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
@staticmethod
def sanity_check_attributes(
    positions: TransBuf,
    sizes: TransBuf,
    face_colors: TransBuf,
    edge_colors: TransBuf,
    edge_widths: TransBuf,
) -> None:
    """Check that the attributes are valid and consistent.

    Args:
        positions: Positions of the points.
        sizes: Sizes of the points.
        face_colors: Face colors of the points.
        edge_colors: Edge colors of the points.
        edge_widths: Edge widths of the points.
    """
    pass

sanity_check_attributes_buffer(positions: Buffer, sizes: Buffer, face_colors: Buffer, edge_colors: Buffer, edge_widths: Buffer) staticmethod

Same as .sanity_check_attributes() but accept only Buffers.

  • It is meant to be used after converting TransBuf to Buffer.
Source code in src/gsp/visuals/points.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@staticmethod
def sanity_check_attributes_buffer(positions: Buffer, sizes: Buffer, face_colors: Buffer, edge_colors: Buffer, edge_widths: Buffer):
    """Same as .sanity_check_attributes() but accept only Buffers.

    - It is meant to be used after converting TransBuf to Buffer.
    """
    # sanity check - each attribute must be a Buffer (not a transform chain)
    assert isinstance(positions, Buffer), "Positions must be a Buffer"
    assert isinstance(sizes, Buffer), "Sizes must be a Buffer"
    assert isinstance(face_colors, Buffer), "Face colors must be a Buffer"
    assert isinstance(edge_colors, Buffer), "Edge colors must be a Buffer"
    assert isinstance(edge_widths, Buffer), "Edge widths must be a Buffer"

    Points.sanity_check_attributes(positions, sizes, face_colors, edge_colors, edge_widths)

set_attributes(positions: TransBuf | None = None, sizes: TransBuf | None = None, face_colors: TransBuf | None = None, edge_colors: TransBuf | None = None, edge_widths: TransBuf | None = None) -> None

Set multiple attributes at once and then check their validity.

Source code in src/gsp/visuals/points.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def set_attributes(
    self,
    positions: TransBuf | None = None,
    sizes: TransBuf | None = None,
    face_colors: TransBuf | None = None,
    edge_colors: TransBuf | None = None,
    edge_widths: TransBuf | None = None,
) -> None:
    """Set multiple attributes at once and then check their validity."""
    if positions is not None:
        self._positions = positions
    if sizes is not None:
        self._sizes = sizes
    if face_colors is not None:
        self._face_colors = face_colors
    if edge_colors is not None:
        self._edge_colors = edge_colors
    if edge_widths is not None:
        self._edge_widths = edge_widths

    self.check_attributes()

set_edge_colors(edge_colors: TransBuf) -> None

Set edge colors of the points.

Parameters:

Name Type Description Default
edge_colors gsp.types.transbuf.TransBuf

New edge colors for the points.

required
Source code in src/gsp/visuals/points.py
84
85
86
87
88
89
90
91
def set_edge_colors(self, edge_colors: TransBuf) -> None:
    """Set edge colors of the points.

    Args:
        edge_colors: New edge colors for the points.
    """
    self._edge_colors = edge_colors
    self.check_attributes()

set_edge_widths(edge_widths: TransBuf) -> None

Set edge widths of the points.

Parameters:

Name Type Description Default
edge_widths gsp.types.transbuf.TransBuf

New edge widths for the points.

required
Source code in src/gsp/visuals/points.py
 97
 98
 99
100
101
102
103
104
def set_edge_widths(self, edge_widths: TransBuf) -> None:
    """Set edge widths of the points.

    Args:
        edge_widths: New edge widths for the points.
    """
    self._edge_widths = edge_widths
    self.check_attributes()

set_face_colors(face_colors: TransBuf) -> None

Set face colors of the points.

Parameters:

Name Type Description Default
face_colors gsp.types.transbuf.TransBuf

New face colors for the points.

required
Source code in src/gsp/visuals/points.py
71
72
73
74
75
76
77
78
def set_face_colors(self, face_colors: TransBuf) -> None:
    """Set face colors of the points.

    Args:
        face_colors: New face colors for the points.
    """
    self._face_colors = face_colors
    self.check_attributes()

set_positions(positions: TransBuf) -> None

Set positions of the points.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

New positions for the points.

required
Source code in src/gsp/visuals/points.py
45
46
47
48
49
50
51
52
def set_positions(self, positions: TransBuf) -> None:
    """Set positions of the points.

    Args:
        positions: New positions for the points.
    """
    self._positions = positions
    self.check_attributes()

set_sizes(sizes: TransBuf) -> None

Set sizes of the points.

Parameters:

Name Type Description Default
sizes gsp.types.transbuf.TransBuf

New sizes for the points.

required
Source code in src/gsp/visuals/points.py
58
59
60
61
62
63
64
65
def set_sizes(self, sizes: TransBuf) -> None:
    """Set sizes of the points.

    Args:
        sizes: New sizes for the points.
    """
    self._sizes = sizes
    self.check_attributes()

gsp.visuals.markers

Marker visual for rendering 2D/3D markers with customizable shapes, sizes, and colors.

Markers

Bases: gsp.types.visual_base.VisualBase

Visual representation of markers with configurable properties.

This class manages marker visualization with properties including shape, positions, sizes, face colors, edge colors, and edge widths.

Attributes:

Name Type Description
_marker_shape gsp.types.marker_shape.MarkerShape

The shape of the markers.

_positions gsp.types.transbuf.TransBuf

The positions of the markers.

_sizes gsp.types.transbuf.TransBuf

The sizes of the markers.

_face_colors gsp.types.transbuf.TransBuf

The face colors of the markers.

_edge_colors gsp.types.transbuf.TransBuf

The edge colors of the markers.

_edge_widths gsp.types.transbuf.TransBuf

The edge widths of the markers.

Source code in src/gsp/visuals/markers.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
class Markers(VisualBase):
    """Visual representation of markers with configurable properties.

    This class manages marker visualization with properties including shape,
    positions, sizes, face colors, edge colors, and edge widths.

    Attributes:
        _marker_shape (MarkerShape): The shape of the markers.
        _positions (TransBuf): The positions of the markers.
        _sizes (TransBuf): The sizes of the markers.
        _face_colors (TransBuf): The face colors of the markers.
        _edge_colors (TransBuf): The edge colors of the markers.
        _edge_widths (TransBuf): The edge widths of the markers.
    """
    __slots__ = ["_marker_shape", "_positions", "_sizes", "_face_colors", "_edge_colors", "_edge_widths"]

    def __init__(self, marker_shape: MarkerShape, positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf):
        """Initialize a Markers visual.

        Args:
            marker_shape (MarkerShape): The shape of the markers.
            positions (TransBuf): The positions of the markers.
            sizes (TransBuf): The sizes of the markers.
            face_colors (TransBuf): The face colors of the markers.
            edge_colors (TransBuf): The edge colors of the markers.
            edge_widths (TransBuf): The edge widths of the markers.
        """
        super().__init__()

        self._marker_shape: MarkerShape = marker_shape
        self._positions: TransBuf = positions
        self._sizes: TransBuf = sizes
        self._face_colors: TransBuf = face_colors
        self._edge_colors: TransBuf = edge_colors
        self._edge_widths: TransBuf = edge_widths

        self.check_attributes()

    # =============================================================================
    # get/set attributes
    # =============================================================================

    def get_marker_shape(self) -> MarkerShape:
        """Get the marker shape.

        Returns:
            MarkerShape: The marker shape.
        """
        return self._marker_shape

    def set_marker_shape(self, marker_shape: MarkerShape) -> None:
        """Set the marker shape.

        Args:
            marker_shape (MarkerShape): The new marker shape.
        """
        self._marker_shape = marker_shape
        self.check_attributes()

    def get_positions(self) -> TransBuf:
        """Get the marker positions.

        Returns:
            TransBuf: The marker positions.
        """
        return self._positions

    def set_positions(self, positions: TransBuf) -> None:
        """Set the marker positions.

        Args:
            positions (TransBuf): The new marker positions.
        """
        self._positions = positions
        self.check_attributes()

    def get_sizes(self) -> TransBuf:
        """Get the marker sizes.

        Returns:
            TransBuf: The marker sizes.
        """
        return self._sizes

    def set_sizes(self, sizes: TransBuf) -> None:
        """Set the marker sizes.

        Args:
            sizes (TransBuf): The new marker sizes.
        """
        self._sizes = sizes
        self.check_attributes()

    def get_face_colors(self) -> TransBuf:
        """Get the marker face colors.

        Returns:
            TransBuf: The marker face colors.
        """
        return self._face_colors

    def set_face_colors(self, face_colors: TransBuf) -> None:
        """Set the marker face colors.

        Args:
            face_colors (TransBuf): The new marker face colors.
        """
        self._face_colors = face_colors
        self.check_attributes()

    def get_edge_colors(self) -> TransBuf:
        """Get the marker edge colors.

        Returns:
            TransBuf: The marker edge colors.
        """
        return self._edge_colors

    def set_edge_colors(self, edge_colors: TransBuf) -> None:
        """Set the marker edge colors.

        Args:
            edge_colors (TransBuf): The new marker edge colors.
        """
        self._edge_colors = edge_colors
        self.check_attributes()

    def get_edge_widths(self) -> TransBuf:
        """Get the marker edge widths.

        Returns:
            TransBuf: The marker edge widths.
        """
        return self._edge_widths

    def set_edge_widths(self, edge_widths: TransBuf) -> None:
        """Set the marker edge widths.

        Args:
            edge_widths (TransBuf): The new marker edge widths.
        """
        self._edge_widths = edge_widths
        self.check_attributes()

    def set_attributes(
        self,
        marker_shape: MarkerShape | None = None,
        positions: TransBuf | None = None,
        sizes: TransBuf | None = None,
        face_colors: TransBuf | None = None,
        edge_colors: TransBuf | None = None,
        edge_widths: TransBuf | None = None,
    ) -> None:
        """Set multiple attributes at once and then check their validity."""
        if marker_shape is not None:
            self._marker_shape = marker_shape
        if positions is not None:
            self._positions = positions
        if sizes is not None:
            self._sizes = sizes
        if face_colors is not None:
            self._face_colors = face_colors
        if edge_colors is not None:
            self._edge_colors = edge_colors
        if edge_widths is not None:
            self._edge_widths = edge_widths
        self.check_attributes()

    # =============================================================================
    # Sanity check functions
    # =============================================================================

    def check_attributes(self) -> None:
        """Check that the attributes are valid and consistent."""
        self.sanity_check_attributes(self._marker_shape, self._positions, self._sizes, self._face_colors, self._edge_colors, self._edge_widths)

    @staticmethod
    def sanity_check_attributes_buffer(
        marker_shape: MarkerShape, positions: Buffer, sizes: Buffer, face_colors: Buffer, edge_colors: Buffer, edge_widths: Buffer
    ):
        """Same as .sanity_check_attributes() but accept only Buffers.

        This method is meant to be used after converting TransBuf to Buffer.

        Args:
            marker_shape (MarkerShape): The marker shape.
            positions (Buffer): The marker positions as a Buffer.
            sizes (Buffer): The marker sizes as a Buffer.
            face_colors (Buffer): The marker face colors as a Buffer.
            edge_colors (Buffer): The marker edge colors as a Buffer.
            edge_widths (Buffer): The marker edge widths as a Buffer.
        """
        # sanity check - each attribute must be a Buffer (not a transform chain)
        assert isinstance(positions, Buffer), "Positions must be a Buffer"
        assert isinstance(sizes, Buffer), "Sizes must be a Buffer"
        assert isinstance(face_colors, Buffer), "Face colors must be a Buffer"
        assert isinstance(edge_colors, Buffer), "Edge colors must be a Buffer"
        assert isinstance(edge_widths, Buffer), "Edge widths must be a Buffer"

        Markers.sanity_check_attributes(marker_shape, positions, sizes, face_colors, edge_colors, edge_widths)

    @staticmethod
    def sanity_check_attributes(
        marker_shape: MarkerShape,
        positions: TransBuf,
        sizes: TransBuf,
        face_colors: TransBuf,
        edge_colors: TransBuf,
        edge_widths: TransBuf,
    ) -> None:
        """Check that the marker attributes are valid and consistent.

        Args:
            marker_shape (MarkerShape): The marker shape.
            positions (TransBuf): The marker positions.
            sizes (TransBuf): The marker sizes.
            face_colors (TransBuf): The marker face colors.
            edge_colors (TransBuf): The marker edge colors.
            edge_widths (TransBuf): The marker edge widths.
        """
        pass

__init__(marker_shape: MarkerShape, positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf)

Initialize a Markers visual.

Parameters:

Name Type Description Default
marker_shape gsp.types.marker_shape.MarkerShape

The shape of the markers.

required
positions gsp.types.transbuf.TransBuf

The positions of the markers.

required
sizes gsp.types.transbuf.TransBuf

The sizes of the markers.

required
face_colors gsp.types.transbuf.TransBuf

The face colors of the markers.

required
edge_colors gsp.types.transbuf.TransBuf

The edge colors of the markers.

required
edge_widths gsp.types.transbuf.TransBuf

The edge widths of the markers.

required
Source code in src/gsp/visuals/markers.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def __init__(self, marker_shape: MarkerShape, positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf):
    """Initialize a Markers visual.

    Args:
        marker_shape (MarkerShape): The shape of the markers.
        positions (TransBuf): The positions of the markers.
        sizes (TransBuf): The sizes of the markers.
        face_colors (TransBuf): The face colors of the markers.
        edge_colors (TransBuf): The edge colors of the markers.
        edge_widths (TransBuf): The edge widths of the markers.
    """
    super().__init__()

    self._marker_shape: MarkerShape = marker_shape
    self._positions: TransBuf = positions
    self._sizes: TransBuf = sizes
    self._face_colors: TransBuf = face_colors
    self._edge_colors: TransBuf = edge_colors
    self._edge_widths: TransBuf = edge_widths

    self.check_attributes()

check_attributes() -> None

Check that the attributes are valid and consistent.

Source code in src/gsp/visuals/markers.py
182
183
184
def check_attributes(self) -> None:
    """Check that the attributes are valid and consistent."""
    self.sanity_check_attributes(self._marker_shape, self._positions, self._sizes, self._face_colors, self._edge_colors, self._edge_widths)

get_edge_colors() -> TransBuf

Get the marker edge colors.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The marker edge colors.

Source code in src/gsp/visuals/markers.py
120
121
122
123
124
125
126
def get_edge_colors(self) -> TransBuf:
    """Get the marker edge colors.

    Returns:
        TransBuf: The marker edge colors.
    """
    return self._edge_colors

get_edge_widths() -> TransBuf

Get the marker edge widths.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The marker edge widths.

Source code in src/gsp/visuals/markers.py
137
138
139
140
141
142
143
def get_edge_widths(self) -> TransBuf:
    """Get the marker edge widths.

    Returns:
        TransBuf: The marker edge widths.
    """
    return self._edge_widths

get_face_colors() -> TransBuf

Get the marker face colors.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The marker face colors.

Source code in src/gsp/visuals/markers.py
103
104
105
106
107
108
109
def get_face_colors(self) -> TransBuf:
    """Get the marker face colors.

    Returns:
        TransBuf: The marker face colors.
    """
    return self._face_colors

get_marker_shape() -> MarkerShape

Get the marker shape.

Returns:

Name Type Description
MarkerShape gsp.types.marker_shape.MarkerShape

The marker shape.

Source code in src/gsp/visuals/markers.py
52
53
54
55
56
57
58
def get_marker_shape(self) -> MarkerShape:
    """Get the marker shape.

    Returns:
        MarkerShape: The marker shape.
    """
    return self._marker_shape

get_positions() -> TransBuf

Get the marker positions.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The marker positions.

Source code in src/gsp/visuals/markers.py
69
70
71
72
73
74
75
def get_positions(self) -> TransBuf:
    """Get the marker positions.

    Returns:
        TransBuf: The marker positions.
    """
    return self._positions

get_sizes() -> TransBuf

Get the marker sizes.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

The marker sizes.

Source code in src/gsp/visuals/markers.py
86
87
88
89
90
91
92
def get_sizes(self) -> TransBuf:
    """Get the marker sizes.

    Returns:
        TransBuf: The marker sizes.
    """
    return self._sizes

sanity_check_attributes(marker_shape: MarkerShape, positions: TransBuf, sizes: TransBuf, face_colors: TransBuf, edge_colors: TransBuf, edge_widths: TransBuf) -> None staticmethod

Check that the marker attributes are valid and consistent.

Parameters:

Name Type Description Default
marker_shape gsp.types.marker_shape.MarkerShape

The marker shape.

required
positions gsp.types.transbuf.TransBuf

The marker positions.

required
sizes gsp.types.transbuf.TransBuf

The marker sizes.

required
face_colors gsp.types.transbuf.TransBuf

The marker face colors.

required
edge_colors gsp.types.transbuf.TransBuf

The marker edge colors.

required
edge_widths gsp.types.transbuf.TransBuf

The marker edge widths.

required
Source code in src/gsp/visuals/markers.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
@staticmethod
def sanity_check_attributes(
    marker_shape: MarkerShape,
    positions: TransBuf,
    sizes: TransBuf,
    face_colors: TransBuf,
    edge_colors: TransBuf,
    edge_widths: TransBuf,
) -> None:
    """Check that the marker attributes are valid and consistent.

    Args:
        marker_shape (MarkerShape): The marker shape.
        positions (TransBuf): The marker positions.
        sizes (TransBuf): The marker sizes.
        face_colors (TransBuf): The marker face colors.
        edge_colors (TransBuf): The marker edge colors.
        edge_widths (TransBuf): The marker edge widths.
    """
    pass

sanity_check_attributes_buffer(marker_shape: MarkerShape, positions: Buffer, sizes: Buffer, face_colors: Buffer, edge_colors: Buffer, edge_widths: Buffer) staticmethod

Same as .sanity_check_attributes() but accept only Buffers.

This method is meant to be used after converting TransBuf to Buffer.

Parameters:

Name Type Description Default
marker_shape gsp.types.marker_shape.MarkerShape

The marker shape.

required
positions gsp.types.buffer.Buffer

The marker positions as a Buffer.

required
sizes gsp.types.buffer.Buffer

The marker sizes as a Buffer.

required
face_colors gsp.types.buffer.Buffer

The marker face colors as a Buffer.

required
edge_colors gsp.types.buffer.Buffer

The marker edge colors as a Buffer.

required
edge_widths gsp.types.buffer.Buffer

The marker edge widths as a Buffer.

required
Source code in src/gsp/visuals/markers.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
@staticmethod
def sanity_check_attributes_buffer(
    marker_shape: MarkerShape, positions: Buffer, sizes: Buffer, face_colors: Buffer, edge_colors: Buffer, edge_widths: Buffer
):
    """Same as .sanity_check_attributes() but accept only Buffers.

    This method is meant to be used after converting TransBuf to Buffer.

    Args:
        marker_shape (MarkerShape): The marker shape.
        positions (Buffer): The marker positions as a Buffer.
        sizes (Buffer): The marker sizes as a Buffer.
        face_colors (Buffer): The marker face colors as a Buffer.
        edge_colors (Buffer): The marker edge colors as a Buffer.
        edge_widths (Buffer): The marker edge widths as a Buffer.
    """
    # sanity check - each attribute must be a Buffer (not a transform chain)
    assert isinstance(positions, Buffer), "Positions must be a Buffer"
    assert isinstance(sizes, Buffer), "Sizes must be a Buffer"
    assert isinstance(face_colors, Buffer), "Face colors must be a Buffer"
    assert isinstance(edge_colors, Buffer), "Edge colors must be a Buffer"
    assert isinstance(edge_widths, Buffer), "Edge widths must be a Buffer"

    Markers.sanity_check_attributes(marker_shape, positions, sizes, face_colors, edge_colors, edge_widths)

set_attributes(marker_shape: MarkerShape | None = None, positions: TransBuf | None = None, sizes: TransBuf | None = None, face_colors: TransBuf | None = None, edge_colors: TransBuf | None = None, edge_widths: TransBuf | None = None) -> None

Set multiple attributes at once and then check their validity.

Source code in src/gsp/visuals/markers.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def set_attributes(
    self,
    marker_shape: MarkerShape | None = None,
    positions: TransBuf | None = None,
    sizes: TransBuf | None = None,
    face_colors: TransBuf | None = None,
    edge_colors: TransBuf | None = None,
    edge_widths: TransBuf | None = None,
) -> None:
    """Set multiple attributes at once and then check their validity."""
    if marker_shape is not None:
        self._marker_shape = marker_shape
    if positions is not None:
        self._positions = positions
    if sizes is not None:
        self._sizes = sizes
    if face_colors is not None:
        self._face_colors = face_colors
    if edge_colors is not None:
        self._edge_colors = edge_colors
    if edge_widths is not None:
        self._edge_widths = edge_widths
    self.check_attributes()

set_edge_colors(edge_colors: TransBuf) -> None

Set the marker edge colors.

Parameters:

Name Type Description Default
edge_colors gsp.types.transbuf.TransBuf

The new marker edge colors.

required
Source code in src/gsp/visuals/markers.py
128
129
130
131
132
133
134
135
def set_edge_colors(self, edge_colors: TransBuf) -> None:
    """Set the marker edge colors.

    Args:
        edge_colors (TransBuf): The new marker edge colors.
    """
    self._edge_colors = edge_colors
    self.check_attributes()

set_edge_widths(edge_widths: TransBuf) -> None

Set the marker edge widths.

Parameters:

Name Type Description Default
edge_widths gsp.types.transbuf.TransBuf

The new marker edge widths.

required
Source code in src/gsp/visuals/markers.py
145
146
147
148
149
150
151
152
def set_edge_widths(self, edge_widths: TransBuf) -> None:
    """Set the marker edge widths.

    Args:
        edge_widths (TransBuf): The new marker edge widths.
    """
    self._edge_widths = edge_widths
    self.check_attributes()

set_face_colors(face_colors: TransBuf) -> None

Set the marker face colors.

Parameters:

Name Type Description Default
face_colors gsp.types.transbuf.TransBuf

The new marker face colors.

required
Source code in src/gsp/visuals/markers.py
111
112
113
114
115
116
117
118
def set_face_colors(self, face_colors: TransBuf) -> None:
    """Set the marker face colors.

    Args:
        face_colors (TransBuf): The new marker face colors.
    """
    self._face_colors = face_colors
    self.check_attributes()

set_marker_shape(marker_shape: MarkerShape) -> None

Set the marker shape.

Parameters:

Name Type Description Default
marker_shape gsp.types.marker_shape.MarkerShape

The new marker shape.

required
Source code in src/gsp/visuals/markers.py
60
61
62
63
64
65
66
67
def set_marker_shape(self, marker_shape: MarkerShape) -> None:
    """Set the marker shape.

    Args:
        marker_shape (MarkerShape): The new marker shape.
    """
    self._marker_shape = marker_shape
    self.check_attributes()

set_positions(positions: TransBuf) -> None

Set the marker positions.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

The new marker positions.

required
Source code in src/gsp/visuals/markers.py
77
78
79
80
81
82
83
84
def set_positions(self, positions: TransBuf) -> None:
    """Set the marker positions.

    Args:
        positions (TransBuf): The new marker positions.
    """
    self._positions = positions
    self.check_attributes()

set_sizes(sizes: TransBuf) -> None

Set the marker sizes.

Parameters:

Name Type Description Default
sizes gsp.types.transbuf.TransBuf

The new marker sizes.

required
Source code in src/gsp/visuals/markers.py
 94
 95
 96
 97
 98
 99
100
101
def set_sizes(self, sizes: TransBuf) -> None:
    """Set the marker sizes.

    Args:
        sizes (TransBuf): The new marker sizes.
    """
    self._sizes = sizes
    self.check_attributes()

gsp.visuals.segments

Segments visual module.

Segments

Bases: gsp.types.visual_base.VisualBase

Segments visual for rendering line segments.

This visual represents a collection of line segments with configurable positions, line widths, cap styles, and colors.

Source code in src/gsp/visuals/segments.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
class Segments(VisualBase):
    """Segments visual for rendering line segments.

    This visual represents a collection of line segments with configurable
    positions, line widths, cap styles, and colors.
    """
    __slots__ = ["_positions", "_colors", "_line_widths", "_cap_style"]

    def __init__(self, positions: TransBuf, line_widths: TransBuf, cap_style: CapStyle, colors: TransBuf) -> None:
        """Initialize Segments visual.

        Args:
            positions: Positions of the segment endpoints.
            line_widths: Widths of the line segments.
            cap_style: Cap style for the line segments.
            colors: Colors of the segments.
        """
        super().__init__()

        self._positions: TransBuf = positions
        self._line_widths: TransBuf = line_widths
        self._cap_style: CapStyle = cap_style
        self._colors: TransBuf = colors

    # =============================================================================
    # get/set attributes
    # =============================================================================

    def get_positions(self) -> TransBuf:
        """Get positions of the segment endpoints."""
        return self._positions

    def set_positions(self, positions: TransBuf) -> None:
        """Set positions of the segment endpoints.

        Args:
            positions: New positions for the segments.
        """
        self._positions = positions
        self.check_attributes()

    def get_line_widths(self) -> TransBuf:
        """Get line widths of the segments."""
        return self._line_widths

    def set_line_widths(self, line_widths: TransBuf) -> None:
        """Set line widths of the segments.

        Args:
            line_widths: New line widths for the segments.
        """
        self._line_widths = line_widths
        self.check_attributes()

    def get_cap_style(self) -> CapStyle:
        """Get cap style of the segments."""
        return self._cap_style

    def set_cap_style(self, cap_style: CapStyle) -> None:
        """Set cap style of the segments.

        Args:
            cap_style: New cap style for the segments.
        """
        self._cap_style = cap_style
        self.check_attributes()

    def get_colors(self) -> TransBuf:
        """Get colors of the segments."""
        return self._colors

    def set_colors(self, colors: TransBuf) -> None:
        """Set colors of the segments.

        Args:
            colors: New colors for the segments.
        """
        self._colors = colors
        self.check_attributes()

    def set_attributes(
        self,
        positions: TransBuf | None = None,
        line_widths: TransBuf | None = None,
        cap_style: CapStyle | None = None,
        colors: TransBuf | None = None,
    ) -> None:
        """Set multiple attributes at once and then check their validity."""
        if positions is not None:
            self._positions = positions
        if line_widths is not None:
            self._line_widths = line_widths
        if cap_style is not None:
            self._cap_style = cap_style
        if colors is not None:
            self._colors = colors
        self.check_attributes()

    # =============================================================================
    # Sanity check functions
    # =============================================================================

    def check_attributes(self) -> None:
        """Check that the attributes are valid and consistent."""
        self.sanity_check_attributes(self._positions, self._line_widths, self._cap_style, self._colors)

    @staticmethod
    def sanity_check_attributes_buffer(positions: Buffer, line_widths: Buffer, cap_style: CapStyle, colors: Buffer) -> None:
        """Same as .sanity_check_attributes() but accept only Buffers.

        - It is meant to be used after converting TransBuf to Buffer.
        """
        # sanity check - each attribute must be a Buffer (not a transform chain)
        assert isinstance(positions, Buffer), "Positions must be a Buffer"
        assert isinstance(line_widths, Buffer), "Line widths must be a Buffer"
        assert isinstance(colors, Buffer), "Colors must be a Buffer"

        Segments.sanity_check_attributes(positions, line_widths, cap_style, colors)

    @staticmethod
    def sanity_check_attributes(
        positions: TransBuf,
        line_widths: TransBuf,
        cap_style: CapStyle,
        colors: TransBuf,
    ) -> None:
        """Check that the attributes are valid and consistent.

        Args:
            positions: Positions of the segment endpoints.
            line_widths: Widths of the line segments.
            cap_style: Cap style for the line segments.
            colors: Colors of the segments.
        """
        pass

__init__(positions: TransBuf, line_widths: TransBuf, cap_style: CapStyle, colors: TransBuf) -> None

Initialize Segments visual.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the segment endpoints.

required
line_widths gsp.types.transbuf.TransBuf

Widths of the line segments.

required
cap_style gsp.types.cap_style.CapStyle

Cap style for the line segments.

required
colors gsp.types.transbuf.TransBuf

Colors of the segments.

required
Source code in src/gsp/visuals/segments.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def __init__(self, positions: TransBuf, line_widths: TransBuf, cap_style: CapStyle, colors: TransBuf) -> None:
    """Initialize Segments visual.

    Args:
        positions: Positions of the segment endpoints.
        line_widths: Widths of the line segments.
        cap_style: Cap style for the line segments.
        colors: Colors of the segments.
    """
    super().__init__()

    self._positions: TransBuf = positions
    self._line_widths: TransBuf = line_widths
    self._cap_style: CapStyle = cap_style
    self._colors: TransBuf = colors

check_attributes() -> None

Check that the attributes are valid and consistent.

Source code in src/gsp/visuals/segments.py
113
114
115
def check_attributes(self) -> None:
    """Check that the attributes are valid and consistent."""
    self.sanity_check_attributes(self._positions, self._line_widths, self._cap_style, self._colors)

get_cap_style() -> CapStyle

Get cap style of the segments.

Source code in src/gsp/visuals/segments.py
65
66
67
def get_cap_style(self) -> CapStyle:
    """Get cap style of the segments."""
    return self._cap_style

get_colors() -> TransBuf

Get colors of the segments.

Source code in src/gsp/visuals/segments.py
78
79
80
def get_colors(self) -> TransBuf:
    """Get colors of the segments."""
    return self._colors

get_line_widths() -> TransBuf

Get line widths of the segments.

Source code in src/gsp/visuals/segments.py
52
53
54
def get_line_widths(self) -> TransBuf:
    """Get line widths of the segments."""
    return self._line_widths

get_positions() -> TransBuf

Get positions of the segment endpoints.

Source code in src/gsp/visuals/segments.py
39
40
41
def get_positions(self) -> TransBuf:
    """Get positions of the segment endpoints."""
    return self._positions

sanity_check_attributes(positions: TransBuf, line_widths: TransBuf, cap_style: CapStyle, colors: TransBuf) -> None staticmethod

Check that the attributes are valid and consistent.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the segment endpoints.

required
line_widths gsp.types.transbuf.TransBuf

Widths of the line segments.

required
cap_style gsp.types.cap_style.CapStyle

Cap style for the line segments.

required
colors gsp.types.transbuf.TransBuf

Colors of the segments.

required
Source code in src/gsp/visuals/segments.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
@staticmethod
def sanity_check_attributes(
    positions: TransBuf,
    line_widths: TransBuf,
    cap_style: CapStyle,
    colors: TransBuf,
) -> None:
    """Check that the attributes are valid and consistent.

    Args:
        positions: Positions of the segment endpoints.
        line_widths: Widths of the line segments.
        cap_style: Cap style for the line segments.
        colors: Colors of the segments.
    """
    pass

sanity_check_attributes_buffer(positions: Buffer, line_widths: Buffer, cap_style: CapStyle, colors: Buffer) -> None staticmethod

Same as .sanity_check_attributes() but accept only Buffers.

  • It is meant to be used after converting TransBuf to Buffer.
Source code in src/gsp/visuals/segments.py
117
118
119
120
121
122
123
124
125
126
127
128
@staticmethod
def sanity_check_attributes_buffer(positions: Buffer, line_widths: Buffer, cap_style: CapStyle, colors: Buffer) -> None:
    """Same as .sanity_check_attributes() but accept only Buffers.

    - It is meant to be used after converting TransBuf to Buffer.
    """
    # sanity check - each attribute must be a Buffer (not a transform chain)
    assert isinstance(positions, Buffer), "Positions must be a Buffer"
    assert isinstance(line_widths, Buffer), "Line widths must be a Buffer"
    assert isinstance(colors, Buffer), "Colors must be a Buffer"

    Segments.sanity_check_attributes(positions, line_widths, cap_style, colors)

set_attributes(positions: TransBuf | None = None, line_widths: TransBuf | None = None, cap_style: CapStyle | None = None, colors: TransBuf | None = None) -> None

Set multiple attributes at once and then check their validity.

Source code in src/gsp/visuals/segments.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def set_attributes(
    self,
    positions: TransBuf | None = None,
    line_widths: TransBuf | None = None,
    cap_style: CapStyle | None = None,
    colors: TransBuf | None = None,
) -> None:
    """Set multiple attributes at once and then check their validity."""
    if positions is not None:
        self._positions = positions
    if line_widths is not None:
        self._line_widths = line_widths
    if cap_style is not None:
        self._cap_style = cap_style
    if colors is not None:
        self._colors = colors
    self.check_attributes()

set_cap_style(cap_style: CapStyle) -> None

Set cap style of the segments.

Parameters:

Name Type Description Default
cap_style gsp.types.cap_style.CapStyle

New cap style for the segments.

required
Source code in src/gsp/visuals/segments.py
69
70
71
72
73
74
75
76
def set_cap_style(self, cap_style: CapStyle) -> None:
    """Set cap style of the segments.

    Args:
        cap_style: New cap style for the segments.
    """
    self._cap_style = cap_style
    self.check_attributes()

set_colors(colors: TransBuf) -> None

Set colors of the segments.

Parameters:

Name Type Description Default
colors gsp.types.transbuf.TransBuf

New colors for the segments.

required
Source code in src/gsp/visuals/segments.py
82
83
84
85
86
87
88
89
def set_colors(self, colors: TransBuf) -> None:
    """Set colors of the segments.

    Args:
        colors: New colors for the segments.
    """
    self._colors = colors
    self.check_attributes()

set_line_widths(line_widths: TransBuf) -> None

Set line widths of the segments.

Parameters:

Name Type Description Default
line_widths gsp.types.transbuf.TransBuf

New line widths for the segments.

required
Source code in src/gsp/visuals/segments.py
56
57
58
59
60
61
62
63
def set_line_widths(self, line_widths: TransBuf) -> None:
    """Set line widths of the segments.

    Args:
        line_widths: New line widths for the segments.
    """
    self._line_widths = line_widths
    self.check_attributes()

set_positions(positions: TransBuf) -> None

Set positions of the segment endpoints.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

New positions for the segments.

required
Source code in src/gsp/visuals/segments.py
43
44
45
46
47
48
49
50
def set_positions(self, positions: TransBuf) -> None:
    """Set positions of the segment endpoints.

    Args:
        positions: New positions for the segments.
    """
    self._positions = positions
    self.check_attributes()

gsp.visuals.paths

Path rendering visual for drawing connected line segments.

This module provides the Paths visual class for rendering continuous paths with customizable line widths, colors, cap styles, and join styles.

Paths

Bases: gsp.types.visual_base.VisualBase

Visual for rendering continuous paths with customizable styling.

Paths are sequences of connected line segments that can have varying colors, line widths, and end/join styling.

Source code in src/gsp/visuals/paths.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
class Paths(VisualBase):
    """Visual for rendering continuous paths with customizable styling.

    Paths are sequences of connected line segments that can have varying
    colors, line widths, and end/join styling.
    """

    __slots__ = ["_positions", "_path_sizes", "_colors", "_line_widths", "_cap_style", "_join_style"]

    def __init__(self, positions: TransBuf, path_sizes: TransBuf, colors: TransBuf, line_widths: TransBuf, cap_style: CapStyle, join_style: JoinStyle) -> None:
        """Initialize the Paths visual.

        Args:
            positions (TransBuf): 3D positions for all path vertices.
            path_sizes (TransBuf): Number of vertices in each path.
            colors (TransBuf): RGBA colors for each path.
            line_widths (TransBuf): Line widths for each path.
            cap_style (CapStyle): Style for line endings (round, square, butt).
            join_style (JoinStyle): Style for line joins (miter, bevel, round).
        """
        super().__init__()

        self._positions: TransBuf = positions
        self._path_sizes: TransBuf = path_sizes
        self._colors: TransBuf = colors
        self._line_widths: TransBuf = line_widths
        self._cap_style: CapStyle = cap_style
        self._join_style: JoinStyle = join_style
        self.check_attributes()

    # =============================================================================
    # get/set attributes
    # =============================================================================

    def get_positions(self) -> TransBuf:
        """Get the positions buffer.

        Returns:
            TransBuf: 3D positions for all path vertices.
        """
        return self._positions

    def set_positions(self, positions: TransBuf) -> None:
        """Set the positions buffer.

        Args:
            positions (TransBuf): 3D positions for all path vertices.
        """
        self._positions = positions
        self.check_attributes()

    def get_path_sizes(self) -> TransBuf:
        """Get the path sizes buffer.

        Returns:
            TransBuf: Number of vertices in each path.
        """
        return self._path_sizes

    def set_path_sizes(self, path_sizes: TransBuf) -> None:
        """Set the path sizes buffer.

        Args:
            path_sizes (TransBuf): Number of vertices in each path.
        """
        self._path_sizes = path_sizes
        self.check_attributes()

    def get_colors(self) -> TransBuf:
        """Get the colors buffer.

        Returns:
            TransBuf: RGBA colors for each path.
        """
        return self._colors

    def set_colors(self, colors: TransBuf) -> None:
        """Set the colors buffer.

        Args:
            colors (TransBuf): RGBA colors for each path.
        """
        self._colors = colors
        self.check_attributes()

    def get_line_widths(self) -> TransBuf:
        """Get the line widths buffer.

        Returns:
            TransBuf: Line widths for each path.
        """
        return self._line_widths

    def set_line_widths(self, line_widths: TransBuf) -> None:
        """Set the line widths buffer.

        Args:
            line_widths (TransBuf): Line widths for each path.
        """
        self._line_widths = line_widths
        self.check_attributes()

    def get_join_style(self) -> JoinStyle:
        """Get the join style.

        Returns:
            JoinStyle: Style for line joins (miter, bevel, round).
        """
        return self._join_style

    def set_join_style(self, join_style: JoinStyle) -> None:
        """Set the join style.

        Args:
            join_style (JoinStyle): Style for line joins (miter, bevel, round).
        """
        self._join_style = join_style
        self.check_attributes()

    def get_cap_style(self) -> CapStyle:
        """Get the cap style.

        Returns:
            CapStyle: Style for line endings (round, square, butt).
        """
        return self._cap_style

    def set_cap_style(self, cap_style: CapStyle) -> None:
        """Set the cap style.

        Args:
            cap_style (CapStyle): Style for line endings (round, square, butt).
        """
        self._cap_style = cap_style
        self.check_attributes()

    def set_attributes(
        self,
        positions: TransBuf | None = None,
        path_sizes: TransBuf | None = None,
        colors: TransBuf | None = None,
        line_widths: TransBuf | None = None,
        cap_style: CapStyle | None = None,
        join_style: JoinStyle | None = None,
    ) -> None:
        """Set multiple attributes at once and then check their validity.

        Args:
            positions (TransBuf | None, optional): 3D positions for all path vertices.
            path_sizes (TransBuf | None, optional): Number of vertices in each path.
            colors (TransBuf | None, optional): RGBA colors for each path.
            line_widths (TransBuf | None, optional): Line widths for each path.
            cap_style (CapStyle | None, optional): Style for line endings (round, square, butt).
            join_style (JoinStyle | None, optional): Style for line joins (miter, bevel, round).
        """
        if positions is not None:
            self._positions = positions
        if path_sizes is not None:
            self._path_sizes = path_sizes
        if colors is not None:
            self._colors = colors
        if line_widths is not None:
            self._line_widths = line_widths
        if cap_style is not None:
            self._cap_style = cap_style
        if join_style is not None:
            self._join_style = join_style
        self.check_attributes()

    # =============================================================================
    # Sanity check functions
    # =============================================================================

    def check_attributes(self) -> None:
        """Check that the attributes are valid and consistent."""
        self.sanity_check_attributes(self._positions, self._path_sizes, self._colors, self._line_widths, self._cap_style, self._join_style)

    @staticmethod
    def sanity_check_attributes_buffer(
        positions: Buffer, path_sizes: Buffer, colors: Buffer, line_widths: Buffer, cap_style: CapStyle, join_style: JoinStyle
    ) -> None:
        """Same as .sanity_check_attributes() but accept only Buffers.

        It is meant to be used after converting TransBuf to Buffer.

        Args:
            positions (Buffer): 3D positions for all path vertices.
            path_sizes (Buffer): Number of vertices in each path.
            colors (Buffer): RGBA colors for each path.
            line_widths (Buffer): Line widths for each path.
            cap_style (CapStyle): Style for line endings.
            join_style (JoinStyle): Style for line joins.
        """
        # sanity check - each attribute must be a Buffer (not a transform chain)
        assert isinstance(positions, Buffer), "Positions must be a Buffer"
        assert isinstance(path_sizes, Buffer), "Path sizes must be a Buffer"
        assert isinstance(colors, Buffer), "Colors must be a Buffer"
        assert isinstance(line_widths, Buffer), "Line widths must be a Buffer"

        Paths.sanity_check_attributes(positions, path_sizes, colors, line_widths, cap_style, join_style)

    @staticmethod
    def sanity_check_attributes(
        positions: TransBuf,
        path_sizes: TransBuf,
        colors: TransBuf,
        line_widths: TransBuf,
        cap_style: CapStyle,
        join_style: JoinStyle,
    ) -> None:
        """Validate attribute dimensions and compatibility.

        Args:
            positions (TransBuf): 3D positions for all path vertices.
            path_sizes (TransBuf): Number of vertices in each path.
            colors (TransBuf): RGBA colors for each path.
            line_widths (TransBuf): Line widths for each path.
            cap_style (CapStyle): Style for line endings.
            join_style (JoinStyle): Style for line joins.
        """
        pass

__init__(positions: TransBuf, path_sizes: TransBuf, colors: TransBuf, line_widths: TransBuf, cap_style: CapStyle, join_style: JoinStyle) -> None

Initialize the Paths visual.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

3D positions for all path vertices.

required
path_sizes gsp.types.transbuf.TransBuf

Number of vertices in each path.

required
colors gsp.types.transbuf.TransBuf

RGBA colors for each path.

required
line_widths gsp.types.transbuf.TransBuf

Line widths for each path.

required
cap_style gsp.types.cap_style.CapStyle

Style for line endings (round, square, butt).

required
join_style gsp.types.join_style.JoinStyle

Style for line joins (miter, bevel, round).

required
Source code in src/gsp/visuals/paths.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def __init__(self, positions: TransBuf, path_sizes: TransBuf, colors: TransBuf, line_widths: TransBuf, cap_style: CapStyle, join_style: JoinStyle) -> None:
    """Initialize the Paths visual.

    Args:
        positions (TransBuf): 3D positions for all path vertices.
        path_sizes (TransBuf): Number of vertices in each path.
        colors (TransBuf): RGBA colors for each path.
        line_widths (TransBuf): Line widths for each path.
        cap_style (CapStyle): Style for line endings (round, square, butt).
        join_style (JoinStyle): Style for line joins (miter, bevel, round).
    """
    super().__init__()

    self._positions: TransBuf = positions
    self._path_sizes: TransBuf = path_sizes
    self._colors: TransBuf = colors
    self._line_widths: TransBuf = line_widths
    self._cap_style: CapStyle = cap_style
    self._join_style: JoinStyle = join_style
    self.check_attributes()

check_attributes() -> None

Check that the attributes are valid and consistent.

Source code in src/gsp/visuals/paths.py
188
189
190
def check_attributes(self) -> None:
    """Check that the attributes are valid and consistent."""
    self.sanity_check_attributes(self._positions, self._path_sizes, self._colors, self._line_widths, self._cap_style, self._join_style)

get_cap_style() -> CapStyle

Get the cap style.

Returns:

Name Type Description
CapStyle gsp.types.cap_style.CapStyle

Style for line endings (round, square, butt).

Source code in src/gsp/visuals/paths.py
134
135
136
137
138
139
140
def get_cap_style(self) -> CapStyle:
    """Get the cap style.

    Returns:
        CapStyle: Style for line endings (round, square, butt).
    """
    return self._cap_style

get_colors() -> TransBuf

Get the colors buffer.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

RGBA colors for each path.

Source code in src/gsp/visuals/paths.py
83
84
85
86
87
88
89
def get_colors(self) -> TransBuf:
    """Get the colors buffer.

    Returns:
        TransBuf: RGBA colors for each path.
    """
    return self._colors

get_join_style() -> JoinStyle

Get the join style.

Returns:

Name Type Description
JoinStyle gsp.types.join_style.JoinStyle

Style for line joins (miter, bevel, round).

Source code in src/gsp/visuals/paths.py
117
118
119
120
121
122
123
def get_join_style(self) -> JoinStyle:
    """Get the join style.

    Returns:
        JoinStyle: Style for line joins (miter, bevel, round).
    """
    return self._join_style

get_line_widths() -> TransBuf

Get the line widths buffer.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

Line widths for each path.

Source code in src/gsp/visuals/paths.py
100
101
102
103
104
105
106
def get_line_widths(self) -> TransBuf:
    """Get the line widths buffer.

    Returns:
        TransBuf: Line widths for each path.
    """
    return self._line_widths

get_path_sizes() -> TransBuf

Get the path sizes buffer.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

Number of vertices in each path.

Source code in src/gsp/visuals/paths.py
66
67
68
69
70
71
72
def get_path_sizes(self) -> TransBuf:
    """Get the path sizes buffer.

    Returns:
        TransBuf: Number of vertices in each path.
    """
    return self._path_sizes

get_positions() -> TransBuf

Get the positions buffer.

Returns:

Name Type Description
TransBuf gsp.types.transbuf.TransBuf

3D positions for all path vertices.

Source code in src/gsp/visuals/paths.py
49
50
51
52
53
54
55
def get_positions(self) -> TransBuf:
    """Get the positions buffer.

    Returns:
        TransBuf: 3D positions for all path vertices.
    """
    return self._positions

sanity_check_attributes(positions: TransBuf, path_sizes: TransBuf, colors: TransBuf, line_widths: TransBuf, cap_style: CapStyle, join_style: JoinStyle) -> None staticmethod

Validate attribute dimensions and compatibility.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

3D positions for all path vertices.

required
path_sizes gsp.types.transbuf.TransBuf

Number of vertices in each path.

required
colors gsp.types.transbuf.TransBuf

RGBA colors for each path.

required
line_widths gsp.types.transbuf.TransBuf

Line widths for each path.

required
cap_style gsp.types.cap_style.CapStyle

Style for line endings.

required
join_style gsp.types.join_style.JoinStyle

Style for line joins.

required
Source code in src/gsp/visuals/paths.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
@staticmethod
def sanity_check_attributes(
    positions: TransBuf,
    path_sizes: TransBuf,
    colors: TransBuf,
    line_widths: TransBuf,
    cap_style: CapStyle,
    join_style: JoinStyle,
) -> None:
    """Validate attribute dimensions and compatibility.

    Args:
        positions (TransBuf): 3D positions for all path vertices.
        path_sizes (TransBuf): Number of vertices in each path.
        colors (TransBuf): RGBA colors for each path.
        line_widths (TransBuf): Line widths for each path.
        cap_style (CapStyle): Style for line endings.
        join_style (JoinStyle): Style for line joins.
    """
    pass

sanity_check_attributes_buffer(positions: Buffer, path_sizes: Buffer, colors: Buffer, line_widths: Buffer, cap_style: CapStyle, join_style: JoinStyle) -> None staticmethod

Same as .sanity_check_attributes() but accept only Buffers.

It is meant to be used after converting TransBuf to Buffer.

Parameters:

Name Type Description Default
positions gsp.types.buffer.Buffer

3D positions for all path vertices.

required
path_sizes gsp.types.buffer.Buffer

Number of vertices in each path.

required
colors gsp.types.buffer.Buffer

RGBA colors for each path.

required
line_widths gsp.types.buffer.Buffer

Line widths for each path.

required
cap_style gsp.types.cap_style.CapStyle

Style for line endings.

required
join_style gsp.types.join_style.JoinStyle

Style for line joins.

required
Source code in src/gsp/visuals/paths.py
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
@staticmethod
def sanity_check_attributes_buffer(
    positions: Buffer, path_sizes: Buffer, colors: Buffer, line_widths: Buffer, cap_style: CapStyle, join_style: JoinStyle
) -> None:
    """Same as .sanity_check_attributes() but accept only Buffers.

    It is meant to be used after converting TransBuf to Buffer.

    Args:
        positions (Buffer): 3D positions for all path vertices.
        path_sizes (Buffer): Number of vertices in each path.
        colors (Buffer): RGBA colors for each path.
        line_widths (Buffer): Line widths for each path.
        cap_style (CapStyle): Style for line endings.
        join_style (JoinStyle): Style for line joins.
    """
    # sanity check - each attribute must be a Buffer (not a transform chain)
    assert isinstance(positions, Buffer), "Positions must be a Buffer"
    assert isinstance(path_sizes, Buffer), "Path sizes must be a Buffer"
    assert isinstance(colors, Buffer), "Colors must be a Buffer"
    assert isinstance(line_widths, Buffer), "Line widths must be a Buffer"

    Paths.sanity_check_attributes(positions, path_sizes, colors, line_widths, cap_style, join_style)

set_attributes(positions: TransBuf | None = None, path_sizes: TransBuf | None = None, colors: TransBuf | None = None, line_widths: TransBuf | None = None, cap_style: CapStyle | None = None, join_style: JoinStyle | None = None) -> None

Set multiple attributes at once and then check their validity.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf | None

3D positions for all path vertices.

None
path_sizes gsp.types.transbuf.TransBuf | None

Number of vertices in each path.

None
colors gsp.types.transbuf.TransBuf | None

RGBA colors for each path.

None
line_widths gsp.types.transbuf.TransBuf | None

Line widths for each path.

None
cap_style gsp.types.cap_style.CapStyle | None

Style for line endings (round, square, butt).

None
join_style gsp.types.join_style.JoinStyle | None

Style for line joins (miter, bevel, round).

None
Source code in src/gsp/visuals/paths.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def set_attributes(
    self,
    positions: TransBuf | None = None,
    path_sizes: TransBuf | None = None,
    colors: TransBuf | None = None,
    line_widths: TransBuf | None = None,
    cap_style: CapStyle | None = None,
    join_style: JoinStyle | None = None,
) -> None:
    """Set multiple attributes at once and then check their validity.

    Args:
        positions (TransBuf | None, optional): 3D positions for all path vertices.
        path_sizes (TransBuf | None, optional): Number of vertices in each path.
        colors (TransBuf | None, optional): RGBA colors for each path.
        line_widths (TransBuf | None, optional): Line widths for each path.
        cap_style (CapStyle | None, optional): Style for line endings (round, square, butt).
        join_style (JoinStyle | None, optional): Style for line joins (miter, bevel, round).
    """
    if positions is not None:
        self._positions = positions
    if path_sizes is not None:
        self._path_sizes = path_sizes
    if colors is not None:
        self._colors = colors
    if line_widths is not None:
        self._line_widths = line_widths
    if cap_style is not None:
        self._cap_style = cap_style
    if join_style is not None:
        self._join_style = join_style
    self.check_attributes()

set_cap_style(cap_style: CapStyle) -> None

Set the cap style.

Parameters:

Name Type Description Default
cap_style gsp.types.cap_style.CapStyle

Style for line endings (round, square, butt).

required
Source code in src/gsp/visuals/paths.py
142
143
144
145
146
147
148
149
def set_cap_style(self, cap_style: CapStyle) -> None:
    """Set the cap style.

    Args:
        cap_style (CapStyle): Style for line endings (round, square, butt).
    """
    self._cap_style = cap_style
    self.check_attributes()

set_colors(colors: TransBuf) -> None

Set the colors buffer.

Parameters:

Name Type Description Default
colors gsp.types.transbuf.TransBuf

RGBA colors for each path.

required
Source code in src/gsp/visuals/paths.py
91
92
93
94
95
96
97
98
def set_colors(self, colors: TransBuf) -> None:
    """Set the colors buffer.

    Args:
        colors (TransBuf): RGBA colors for each path.
    """
    self._colors = colors
    self.check_attributes()

set_join_style(join_style: JoinStyle) -> None

Set the join style.

Parameters:

Name Type Description Default
join_style gsp.types.join_style.JoinStyle

Style for line joins (miter, bevel, round).

required
Source code in src/gsp/visuals/paths.py
125
126
127
128
129
130
131
132
def set_join_style(self, join_style: JoinStyle) -> None:
    """Set the join style.

    Args:
        join_style (JoinStyle): Style for line joins (miter, bevel, round).
    """
    self._join_style = join_style
    self.check_attributes()

set_line_widths(line_widths: TransBuf) -> None

Set the line widths buffer.

Parameters:

Name Type Description Default
line_widths gsp.types.transbuf.TransBuf

Line widths for each path.

required
Source code in src/gsp/visuals/paths.py
108
109
110
111
112
113
114
115
def set_line_widths(self, line_widths: TransBuf) -> None:
    """Set the line widths buffer.

    Args:
        line_widths (TransBuf): Line widths for each path.
    """
    self._line_widths = line_widths
    self.check_attributes()

set_path_sizes(path_sizes: TransBuf) -> None

Set the path sizes buffer.

Parameters:

Name Type Description Default
path_sizes gsp.types.transbuf.TransBuf

Number of vertices in each path.

required
Source code in src/gsp/visuals/paths.py
74
75
76
77
78
79
80
81
def set_path_sizes(self, path_sizes: TransBuf) -> None:
    """Set the path sizes buffer.

    Args:
        path_sizes (TransBuf): Number of vertices in each path.
    """
    self._path_sizes = path_sizes
    self.check_attributes()

set_positions(positions: TransBuf) -> None

Set the positions buffer.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

3D positions for all path vertices.

required
Source code in src/gsp/visuals/paths.py
57
58
59
60
61
62
63
64
def set_positions(self, positions: TransBuf) -> None:
    """Set the positions buffer.

    Args:
        positions (TransBuf): 3D positions for all path vertices.
    """
    self._positions = positions
    self.check_attributes()

gsp.visuals.pixels

Pixels visual module.

Pixels

Bases: gsp.types.visual_base.VisualBase

Pixels visual for rendering individual colored pixels.

This visual represents a collection of pixels with configurable positions, colors, and groups for efficient rendering.

Source code in src/gsp/visuals/pixels.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class Pixels(VisualBase):
    """Pixels visual for rendering individual colored pixels.

    This visual represents a collection of pixels with configurable positions,
    colors, and groups for efficient rendering.
    """
    __slots__ = ["__positions", "__colors", "__groups"]

    def __init__(self, positions: TransBuf, colors: TransBuf, groups: Groups):
        """Initialize Pixels visual.

        Args:
            positions: Positions of the pixels.
            colors: Colors of the pixels.
            groups: Groups for organizing pixels.
        """
        super().__init__()

        self.__positions: TransBuf = positions
        self.__colors: TransBuf = colors
        self.__groups: Groups = groups

        self.check_attributes()

    def __repr__(self) -> str:
        """Return string representation of the Pixels visual."""
        return f"Pixels(positions={self.__positions}, colors={self.__colors}, groups={self.__groups})"

    # =============================================================================
    # get/set attributes
    # =============================================================================

    def get_positions(self) -> TransBuf:
        """Get positions of the pixels."""
        return self.__positions

    def set_positions(self, positions: TransBuf) -> None:
        """Set positions of the pixels.

        Args:
            positions: New positions for the pixels.
        """
        self.__positions = positions
        self.check_attributes()

    def get_colors(self) -> TransBuf:
        """Get colors of the pixels."""
        return self.__colors

    def set_colors(self, colors: TransBuf) -> None:
        """Set colors of the pixels.

        Args:
            colors: New colors for the pixels.
        """
        self.__colors = colors
        self.check_attributes()

    def get_groups(self) -> Groups:
        """Get groups for organizing pixels."""
        return self.__groups

    def set_groups(self, groups: Groups) -> None:
        """Set groups for organizing pixels.

        Args:
            groups: New groups for the pixels.
        """
        self.__groups = groups
        self.check_attributes()

    def set_attributes(self, positions: TransBuf | None = None, colors: TransBuf | None = None, groups: Groups | None = None) -> None:
        """Set multiple attributes at once and then check their validity."""
        if positions is not None:
            self.__positions = positions
        if colors is not None:
            self.__colors = colors
        if groups is not None:
            self.__groups = groups
        self.check_attributes()

    # =============================================================================
    # Sanity check functions
    # =============================================================================

    def check_attributes(self) -> None:
        """Check that the attributes are valid and consistent."""
        self.sanity_check_attributes(self.__positions, self.__colors, self.__groups)

    @staticmethod
    def sanity_check_attribute_buffers(positions: Buffer, colors: Buffer, groups: Groups):
        """Same as .sanity_check_attributes() but accept only Buffers.

        - It is meant to be used after converting TransBuf to Buffer.
        """
        # sanity check - each attribute must be a Buffer (not a transform chain)
        assert isinstance(positions, Buffer), "Positions must be a Buffer"
        assert isinstance(colors, Buffer), "Colors must be a Buffer"

        Pixels.sanity_check_attributes(positions, colors, groups)

    @staticmethod
    def sanity_check_attributes(positions: TransBuf, colors: TransBuf, groups: Groups):
        """Check that the attributes are valid and consistent.

        Args:
            positions: Positions of the pixels.
            colors: Colors of the pixels.
            groups: Groups for organizing pixels.
        """
        # =============================================================================
        # if any of the attributes is a TransformChain not fully defined, skip the sanity check
        # =============================================================================

        if isinstance(positions, TransformChain) and not positions.is_fully_defined():
            return
        if isinstance(colors, TransformChain) and not colors.is_fully_defined():
            return

        # =============================================================================
        # Check groups
        # =============================================================================

        # get position_count and group_count
        position_count = positions.get_count() if isinstance(positions, Buffer) else positions.get_buffer_count()
        group_count = GroupUtils.get_group_count(position_count, groups)

        # Check groups matches position count
        GroupUtils.sanity_check(position_count, groups)

        # =============================================================================
        # Check each attributes
        # =============================================================================

        # Check colors attribute
        color_count = colors.get_count() if isinstance(colors, Buffer) else colors.get_buffer_count()
        assert color_count == group_count, f"Colors count {color_count} must match group count {group_count}"

__init__(positions: TransBuf, colors: TransBuf, groups: Groups)

Initialize Pixels visual.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the pixels.

required
colors gsp.types.transbuf.TransBuf

Colors of the pixels.

required
groups gsp.types.group.Groups

Groups for organizing pixels.

required
Source code in src/gsp/visuals/pixels.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def __init__(self, positions: TransBuf, colors: TransBuf, groups: Groups):
    """Initialize Pixels visual.

    Args:
        positions: Positions of the pixels.
        colors: Colors of the pixels.
        groups: Groups for organizing pixels.
    """
    super().__init__()

    self.__positions: TransBuf = positions
    self.__colors: TransBuf = colors
    self.__groups: Groups = groups

    self.check_attributes()

__repr__() -> str

Return string representation of the Pixels visual.

Source code in src/gsp/visuals/pixels.py
35
36
37
def __repr__(self) -> str:
    """Return string representation of the Pixels visual."""
    return f"Pixels(positions={self.__positions}, colors={self.__colors}, groups={self.__groups})"

check_attributes() -> None

Check that the attributes are valid and consistent.

Source code in src/gsp/visuals/pixels.py
96
97
98
def check_attributes(self) -> None:
    """Check that the attributes are valid and consistent."""
    self.sanity_check_attributes(self.__positions, self.__colors, self.__groups)

get_colors() -> TransBuf

Get colors of the pixels.

Source code in src/gsp/visuals/pixels.py
56
57
58
def get_colors(self) -> TransBuf:
    """Get colors of the pixels."""
    return self.__colors

get_groups() -> Groups

Get groups for organizing pixels.

Source code in src/gsp/visuals/pixels.py
69
70
71
def get_groups(self) -> Groups:
    """Get groups for organizing pixels."""
    return self.__groups

get_positions() -> TransBuf

Get positions of the pixels.

Source code in src/gsp/visuals/pixels.py
43
44
45
def get_positions(self) -> TransBuf:
    """Get positions of the pixels."""
    return self.__positions

sanity_check_attribute_buffers(positions: Buffer, colors: Buffer, groups: Groups) staticmethod

Same as .sanity_check_attributes() but accept only Buffers.

  • It is meant to be used after converting TransBuf to Buffer.
Source code in src/gsp/visuals/pixels.py
100
101
102
103
104
105
106
107
108
109
110
@staticmethod
def sanity_check_attribute_buffers(positions: Buffer, colors: Buffer, groups: Groups):
    """Same as .sanity_check_attributes() but accept only Buffers.

    - It is meant to be used after converting TransBuf to Buffer.
    """
    # sanity check - each attribute must be a Buffer (not a transform chain)
    assert isinstance(positions, Buffer), "Positions must be a Buffer"
    assert isinstance(colors, Buffer), "Colors must be a Buffer"

    Pixels.sanity_check_attributes(positions, colors, groups)

sanity_check_attributes(positions: TransBuf, colors: TransBuf, groups: Groups) staticmethod

Check that the attributes are valid and consistent.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the pixels.

required
colors gsp.types.transbuf.TransBuf

Colors of the pixels.

required
groups gsp.types.group.Groups

Groups for organizing pixels.

required
Source code in src/gsp/visuals/pixels.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@staticmethod
def sanity_check_attributes(positions: TransBuf, colors: TransBuf, groups: Groups):
    """Check that the attributes are valid and consistent.

    Args:
        positions: Positions of the pixels.
        colors: Colors of the pixels.
        groups: Groups for organizing pixels.
    """
    # =============================================================================
    # if any of the attributes is a TransformChain not fully defined, skip the sanity check
    # =============================================================================

    if isinstance(positions, TransformChain) and not positions.is_fully_defined():
        return
    if isinstance(colors, TransformChain) and not colors.is_fully_defined():
        return

    # =============================================================================
    # Check groups
    # =============================================================================

    # get position_count and group_count
    position_count = positions.get_count() if isinstance(positions, Buffer) else positions.get_buffer_count()
    group_count = GroupUtils.get_group_count(position_count, groups)

    # Check groups matches position count
    GroupUtils.sanity_check(position_count, groups)

    # =============================================================================
    # Check each attributes
    # =============================================================================

    # Check colors attribute
    color_count = colors.get_count() if isinstance(colors, Buffer) else colors.get_buffer_count()
    assert color_count == group_count, f"Colors count {color_count} must match group count {group_count}"

set_attributes(positions: TransBuf | None = None, colors: TransBuf | None = None, groups: Groups | None = None) -> None

Set multiple attributes at once and then check their validity.

Source code in src/gsp/visuals/pixels.py
82
83
84
85
86
87
88
89
90
def set_attributes(self, positions: TransBuf | None = None, colors: TransBuf | None = None, groups: Groups | None = None) -> None:
    """Set multiple attributes at once and then check their validity."""
    if positions is not None:
        self.__positions = positions
    if colors is not None:
        self.__colors = colors
    if groups is not None:
        self.__groups = groups
    self.check_attributes()

set_colors(colors: TransBuf) -> None

Set colors of the pixels.

Parameters:

Name Type Description Default
colors gsp.types.transbuf.TransBuf

New colors for the pixels.

required
Source code in src/gsp/visuals/pixels.py
60
61
62
63
64
65
66
67
def set_colors(self, colors: TransBuf) -> None:
    """Set colors of the pixels.

    Args:
        colors: New colors for the pixels.
    """
    self.__colors = colors
    self.check_attributes()

set_groups(groups: Groups) -> None

Set groups for organizing pixels.

Parameters:

Name Type Description Default
groups gsp.types.group.Groups

New groups for the pixels.

required
Source code in src/gsp/visuals/pixels.py
73
74
75
76
77
78
79
80
def set_groups(self, groups: Groups) -> None:
    """Set groups for organizing pixels.

    Args:
        groups: New groups for the pixels.
    """
    self.__groups = groups
    self.check_attributes()

set_positions(positions: TransBuf) -> None

Set positions of the pixels.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

New positions for the pixels.

required
Source code in src/gsp/visuals/pixels.py
47
48
49
50
51
52
53
54
def set_positions(self, positions: TransBuf) -> None:
    """Set positions of the pixels.

    Args:
        positions: New positions for the pixels.
    """
    self.__positions = positions
    self.check_attributes()

gsp.visuals.texts

Texts visual module.

Texts

Bases: gsp.types.visual_base.VisualBase

Texts visual.

Source code in src/gsp/visuals/texts.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
class Texts(VisualBase):
    """Texts visual."""

    __slots__ = ["_positions", "_strings", "_colors", "_font_sizes", "_anchors", "_angles", "_font_name"]

    def __init__(
        self,
        positions: TransBuf,
        strings: list[str],
        colors: TransBuf,
        font_sizes: TransBuf,
        anchors: TransBuf,
        angles: TransBuf,
        font_name: str,
    ) -> None:
        """Initialize Texts visual.

        Args:
            positions (TransBuf): Positions of the texts.
            strings (list[str]): List of text strings.
            colors (TransBuf): Colors of the texts.
            font_sizes (TransBuf): Font sizes of the texts.
            anchors (TransBuf): Anchor positions of the texts.
            angles (TransBuf): Rotation angles of the texts.
            font_name (str): Font name for the texts.
        """
        super().__init__()

        self._positions: TransBuf = positions
        self._strings: list[str] = strings
        self._colors: TransBuf = colors
        self._font_sizes: TransBuf = font_sizes
        self._anchors: TransBuf = anchors
        self._angles: TransBuf = angles
        self._font_name: str = font_name
        self.check_attributes()

    # =============================================================================
    # get/set attributes
    # =============================================================================

    def get_positions(self) -> TransBuf:
        """Get positions of the texts."""
        return self._positions

    def set_positions(self, positions: TransBuf) -> None:
        """Set positions of the texts.

        Args:
            positions: New positions for the texts.
        """
        self._positions = positions
        self.check_attributes()

    def get_strings(self) -> list[str]:
        """Get text strings."""
        return self._strings

    def set_strings(self, strings: list[str]) -> None:
        """Set text strings.

        Args:
            strings: New text strings.
        """
        self._strings = strings
        self.check_attributes()

    def get_colors(self) -> TransBuf:
        """Get colors of the texts."""
        return self._colors

    def set_colors(self, colors: TransBuf) -> None:
        """Set colors of the texts.

        Args:
            colors: New colors for the texts.
        """
        self._colors = colors
        self.check_attributes()

    def get_font_sizes(self) -> TransBuf:
        """Get font sizes of the texts."""
        return self._font_sizes

    def set_font_sizes(self, font_sizes: TransBuf) -> None:
        """Set font sizes of the texts.

        Args:
            font_sizes: New font sizes for the texts.
        """
        self._font_sizes = font_sizes
        self.check_attributes()

    def get_anchors(self) -> TransBuf:
        """Get anchor positions of the texts."""
        return self._anchors

    def set_anchors(self, anchors: TransBuf) -> None:
        """Set anchor positions of the texts.

        Args:
            anchors: New anchor positions for the texts.
        """
        self._anchors = anchors
        self.check_attributes()

    def get_angles(self) -> TransBuf:
        """Get rotation angles of the texts."""
        return self._angles

    def set_angles(self, angles: TransBuf) -> None:
        """Set rotation angles of the texts.

        Args:
            angles: New rotation angles for the texts.
        """
        self._angles = angles
        self.check_attributes()

    def get_font_name(self) -> str:
        """Get font name used for the texts."""
        return self._font_name

    def set_font_name(self, font_name: str) -> None:
        """Set font name for the texts.

        Args:
            font_name: New font name for the texts.
        """
        self._font_name = font_name
        self.check_attributes()

    def set_attributes(
        self,
        positions: TransBuf | None = None,
        strings: list[str] | None = None,
        colors: TransBuf | None = None,
        font_sizes: TransBuf | None = None,
        anchors: TransBuf | None = None,
        angles: TransBuf | None = None,
        font_name: str | None = None,
    ) -> None:
        """Set multiple attributes at once and then check their validity."""
        if positions is not None:
            self._positions = positions
        if strings is not None:
            self._strings = strings
        if colors is not None:
            self._colors = colors
        if font_sizes is not None:
            self._font_sizes = font_sizes
        if anchors is not None:
            self._anchors = anchors
        if angles is not None:
            self._angles = angles
        if font_name is not None:
            self._font_name = font_name
        self.check_attributes()

    # =============================================================================
    # Sanity check functions
    # =============================================================================

    def check_attributes(self) -> None:
        """Check that the attributes are valid and consistent."""
        self.sanity_check_attributes(self._positions, self._strings, self._colors, self._font_sizes, self._anchors, self._angles, self._font_name)

    @staticmethod
    def sanity_check_attributes_buffer(
        positions: Buffer, strings: list[str], colors: Buffer, font_sizes: Buffer, anchors: Buffer, angles: Buffer, font_name: str
    ) -> None:
        """Same as .sanity_check_attributes() but accept only Buffers.

        - It is meant to be used after converting TransBuf to Buffer.
        """
        # sanity check - each attribute must be a Buffer (not a transform chain)
        assert isinstance(positions, Buffer), "Positions must be a Buffer"
        assert isinstance(strings, list), "Texts must be a list of strings"
        assert isinstance(colors, Buffer), "Colors must be a Buffer"
        assert isinstance(font_sizes, Buffer), "Font sizes must be a Buffer"
        assert isinstance(anchors, Buffer), "Anchors must be a Buffer"
        assert isinstance(angles, Buffer), "Angles must be a Buffer"
        assert isinstance(font_name, str), "Font name must be a string"

        # check positions, colors, font_sizes, anchors, angles have the same length as strings
        assert positions.get_count() == len(
            strings
        ), f"Positions length must match number of strings. Got {positions.get_count()} positions vs {len(strings)} strings"
        assert colors.get_count() == len(strings), f"Colors length must match number of strings. Got {colors.get_count()} colors vs {len(strings)} strings"
        assert font_sizes.get_count() == len(
            strings
        ), f"Font sizes length must match number of strings. Got {font_sizes.get_count()} font sizes vs {len(strings)} strings"
        assert anchors.get_count() == len(strings), f"Anchors length must match number of strings. Got {anchors.get_count()} anchors vs {len(strings)} strings"
        assert angles.get_count() == len(strings), f"Angles length must match number of strings. Got {angles.get_count()} angles vs {len(strings)} strings"

        # check font_name is not empty and is a string
        assert len(font_name) > 0, "Font name must be a non-empty string"
        assert isinstance(font_name, str), "Font name must be a string"

        # check all strings are indeed strings
        for string in strings:
            assert isinstance(string, str), "All elements in strings must be of type str"

    @staticmethod
    def sanity_check_attributes(
        positions: TransBuf,
        strings: list[str],
        colors: TransBuf,
        font_sizes: TransBuf,
        anchors: TransBuf,
        angles: TransBuf,
        font_name: str,
    ) -> None:
        """Check that the attributes are valid and consistent.

        Args:
            positions: Positions of the texts.
            strings: List of text strings.
            colors: Colors of the texts.
            font_sizes: Font sizes of the texts.
            anchors: Anchor positions of the texts.
            angles: Rotation angles of the texts.
            font_name: Font name for the texts.
        """
        pass

__init__(positions: TransBuf, strings: list[str], colors: TransBuf, font_sizes: TransBuf, anchors: TransBuf, angles: TransBuf, font_name: str) -> None

Initialize Texts visual.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the texts.

required
strings list[str]

List of text strings.

required
colors gsp.types.transbuf.TransBuf

Colors of the texts.

required
font_sizes gsp.types.transbuf.TransBuf

Font sizes of the texts.

required
anchors gsp.types.transbuf.TransBuf

Anchor positions of the texts.

required
angles gsp.types.transbuf.TransBuf

Rotation angles of the texts.

required
font_name str

Font name for the texts.

required
Source code in src/gsp/visuals/texts.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def __init__(
    self,
    positions: TransBuf,
    strings: list[str],
    colors: TransBuf,
    font_sizes: TransBuf,
    anchors: TransBuf,
    angles: TransBuf,
    font_name: str,
) -> None:
    """Initialize Texts visual.

    Args:
        positions (TransBuf): Positions of the texts.
        strings (list[str]): List of text strings.
        colors (TransBuf): Colors of the texts.
        font_sizes (TransBuf): Font sizes of the texts.
        anchors (TransBuf): Anchor positions of the texts.
        angles (TransBuf): Rotation angles of the texts.
        font_name (str): Font name for the texts.
    """
    super().__init__()

    self._positions: TransBuf = positions
    self._strings: list[str] = strings
    self._colors: TransBuf = colors
    self._font_sizes: TransBuf = font_sizes
    self._anchors: TransBuf = anchors
    self._angles: TransBuf = angles
    self._font_name: str = font_name
    self.check_attributes()

check_attributes() -> None

Check that the attributes are valid and consistent.

Source code in src/gsp/visuals/texts.py
171
172
173
def check_attributes(self) -> None:
    """Check that the attributes are valid and consistent."""
    self.sanity_check_attributes(self._positions, self._strings, self._colors, self._font_sizes, self._anchors, self._angles, self._font_name)

get_anchors() -> TransBuf

Get anchor positions of the texts.

Source code in src/gsp/visuals/texts.py
101
102
103
def get_anchors(self) -> TransBuf:
    """Get anchor positions of the texts."""
    return self._anchors

get_angles() -> TransBuf

Get rotation angles of the texts.

Source code in src/gsp/visuals/texts.py
114
115
116
def get_angles(self) -> TransBuf:
    """Get rotation angles of the texts."""
    return self._angles

get_colors() -> TransBuf

Get colors of the texts.

Source code in src/gsp/visuals/texts.py
75
76
77
def get_colors(self) -> TransBuf:
    """Get colors of the texts."""
    return self._colors

get_font_name() -> str

Get font name used for the texts.

Source code in src/gsp/visuals/texts.py
127
128
129
def get_font_name(self) -> str:
    """Get font name used for the texts."""
    return self._font_name

get_font_sizes() -> TransBuf

Get font sizes of the texts.

Source code in src/gsp/visuals/texts.py
88
89
90
def get_font_sizes(self) -> TransBuf:
    """Get font sizes of the texts."""
    return self._font_sizes

get_positions() -> TransBuf

Get positions of the texts.

Source code in src/gsp/visuals/texts.py
49
50
51
def get_positions(self) -> TransBuf:
    """Get positions of the texts."""
    return self._positions

get_strings() -> list[str]

Get text strings.

Source code in src/gsp/visuals/texts.py
62
63
64
def get_strings(self) -> list[str]:
    """Get text strings."""
    return self._strings

sanity_check_attributes(positions: TransBuf, strings: list[str], colors: TransBuf, font_sizes: TransBuf, anchors: TransBuf, angles: TransBuf, font_name: str) -> None staticmethod

Check that the attributes are valid and consistent.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

Positions of the texts.

required
strings list[str]

List of text strings.

required
colors gsp.types.transbuf.TransBuf

Colors of the texts.

required
font_sizes gsp.types.transbuf.TransBuf

Font sizes of the texts.

required
anchors gsp.types.transbuf.TransBuf

Anchor positions of the texts.

required
angles gsp.types.transbuf.TransBuf

Rotation angles of the texts.

required
font_name str

Font name for the texts.

required
Source code in src/gsp/visuals/texts.py
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
@staticmethod
def sanity_check_attributes(
    positions: TransBuf,
    strings: list[str],
    colors: TransBuf,
    font_sizes: TransBuf,
    anchors: TransBuf,
    angles: TransBuf,
    font_name: str,
) -> None:
    """Check that the attributes are valid and consistent.

    Args:
        positions: Positions of the texts.
        strings: List of text strings.
        colors: Colors of the texts.
        font_sizes: Font sizes of the texts.
        anchors: Anchor positions of the texts.
        angles: Rotation angles of the texts.
        font_name: Font name for the texts.
    """
    pass

sanity_check_attributes_buffer(positions: Buffer, strings: list[str], colors: Buffer, font_sizes: Buffer, anchors: Buffer, angles: Buffer, font_name: str) -> None staticmethod

Same as .sanity_check_attributes() but accept only Buffers.

  • It is meant to be used after converting TransBuf to Buffer.
Source code in src/gsp/visuals/texts.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
@staticmethod
def sanity_check_attributes_buffer(
    positions: Buffer, strings: list[str], colors: Buffer, font_sizes: Buffer, anchors: Buffer, angles: Buffer, font_name: str
) -> None:
    """Same as .sanity_check_attributes() but accept only Buffers.

    - It is meant to be used after converting TransBuf to Buffer.
    """
    # sanity check - each attribute must be a Buffer (not a transform chain)
    assert isinstance(positions, Buffer), "Positions must be a Buffer"
    assert isinstance(strings, list), "Texts must be a list of strings"
    assert isinstance(colors, Buffer), "Colors must be a Buffer"
    assert isinstance(font_sizes, Buffer), "Font sizes must be a Buffer"
    assert isinstance(anchors, Buffer), "Anchors must be a Buffer"
    assert isinstance(angles, Buffer), "Angles must be a Buffer"
    assert isinstance(font_name, str), "Font name must be a string"

    # check positions, colors, font_sizes, anchors, angles have the same length as strings
    assert positions.get_count() == len(
        strings
    ), f"Positions length must match number of strings. Got {positions.get_count()} positions vs {len(strings)} strings"
    assert colors.get_count() == len(strings), f"Colors length must match number of strings. Got {colors.get_count()} colors vs {len(strings)} strings"
    assert font_sizes.get_count() == len(
        strings
    ), f"Font sizes length must match number of strings. Got {font_sizes.get_count()} font sizes vs {len(strings)} strings"
    assert anchors.get_count() == len(strings), f"Anchors length must match number of strings. Got {anchors.get_count()} anchors vs {len(strings)} strings"
    assert angles.get_count() == len(strings), f"Angles length must match number of strings. Got {angles.get_count()} angles vs {len(strings)} strings"

    # check font_name is not empty and is a string
    assert len(font_name) > 0, "Font name must be a non-empty string"
    assert isinstance(font_name, str), "Font name must be a string"

    # check all strings are indeed strings
    for string in strings:
        assert isinstance(string, str), "All elements in strings must be of type str"

set_anchors(anchors: TransBuf) -> None

Set anchor positions of the texts.

Parameters:

Name Type Description Default
anchors gsp.types.transbuf.TransBuf

New anchor positions for the texts.

required
Source code in src/gsp/visuals/texts.py
105
106
107
108
109
110
111
112
def set_anchors(self, anchors: TransBuf) -> None:
    """Set anchor positions of the texts.

    Args:
        anchors: New anchor positions for the texts.
    """
    self._anchors = anchors
    self.check_attributes()

set_angles(angles: TransBuf) -> None

Set rotation angles of the texts.

Parameters:

Name Type Description Default
angles gsp.types.transbuf.TransBuf

New rotation angles for the texts.

required
Source code in src/gsp/visuals/texts.py
118
119
120
121
122
123
124
125
def set_angles(self, angles: TransBuf) -> None:
    """Set rotation angles of the texts.

    Args:
        angles: New rotation angles for the texts.
    """
    self._angles = angles
    self.check_attributes()

set_attributes(positions: TransBuf | None = None, strings: list[str] | None = None, colors: TransBuf | None = None, font_sizes: TransBuf | None = None, anchors: TransBuf | None = None, angles: TransBuf | None = None, font_name: str | None = None) -> None

Set multiple attributes at once and then check their validity.

Source code in src/gsp/visuals/texts.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def set_attributes(
    self,
    positions: TransBuf | None = None,
    strings: list[str] | None = None,
    colors: TransBuf | None = None,
    font_sizes: TransBuf | None = None,
    anchors: TransBuf | None = None,
    angles: TransBuf | None = None,
    font_name: str | None = None,
) -> None:
    """Set multiple attributes at once and then check their validity."""
    if positions is not None:
        self._positions = positions
    if strings is not None:
        self._strings = strings
    if colors is not None:
        self._colors = colors
    if font_sizes is not None:
        self._font_sizes = font_sizes
    if anchors is not None:
        self._anchors = anchors
    if angles is not None:
        self._angles = angles
    if font_name is not None:
        self._font_name = font_name
    self.check_attributes()

set_colors(colors: TransBuf) -> None

Set colors of the texts.

Parameters:

Name Type Description Default
colors gsp.types.transbuf.TransBuf

New colors for the texts.

required
Source code in src/gsp/visuals/texts.py
79
80
81
82
83
84
85
86
def set_colors(self, colors: TransBuf) -> None:
    """Set colors of the texts.

    Args:
        colors: New colors for the texts.
    """
    self._colors = colors
    self.check_attributes()

set_font_name(font_name: str) -> None

Set font name for the texts.

Parameters:

Name Type Description Default
font_name str

New font name for the texts.

required
Source code in src/gsp/visuals/texts.py
131
132
133
134
135
136
137
138
def set_font_name(self, font_name: str) -> None:
    """Set font name for the texts.

    Args:
        font_name: New font name for the texts.
    """
    self._font_name = font_name
    self.check_attributes()

set_font_sizes(font_sizes: TransBuf) -> None

Set font sizes of the texts.

Parameters:

Name Type Description Default
font_sizes gsp.types.transbuf.TransBuf

New font sizes for the texts.

required
Source code in src/gsp/visuals/texts.py
92
93
94
95
96
97
98
99
def set_font_sizes(self, font_sizes: TransBuf) -> None:
    """Set font sizes of the texts.

    Args:
        font_sizes: New font sizes for the texts.
    """
    self._font_sizes = font_sizes
    self.check_attributes()

set_positions(positions: TransBuf) -> None

Set positions of the texts.

Parameters:

Name Type Description Default
positions gsp.types.transbuf.TransBuf

New positions for the texts.

required
Source code in src/gsp/visuals/texts.py
53
54
55
56
57
58
59
60
def set_positions(self, positions: TransBuf) -> None:
    """Set positions of the texts.

    Args:
        positions: New positions for the texts.
    """
    self._positions = positions
    self.check_attributes()

set_strings(strings: list[str]) -> None

Set text strings.

Parameters:

Name Type Description Default
strings list[str]

New text strings.

required
Source code in src/gsp/visuals/texts.py
66
67
68
69
70
71
72
73
def set_strings(self, strings: list[str]) -> None:
    """Set text strings.

    Args:
        strings: New text strings.
    """
    self._strings = strings
    self.check_attributes()

Transforms Module

gsp.transforms

GSP Transforms Package.

gsp.transforms.transform_chain

Transform chain module for composing transformations.

This module provides the TransformChain class which allows chaining multiple transform links to process data through a series of transformations.

TransformChain

Chain of transformations to apply to data.

Source code in src/gsp/transforms/transform_chain.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
class TransformChain:
    """Chain of transformations to apply to data."""

    __slots__ = ["__links", "__buffer_count", "__buffer_type"]

    def __init__(self, buffer_count: int, buffer_type: BufferType | None) -> None:
        """Initialize a TransformChain.

        Args:
            buffer_count (int): Number of elements in the output Buffer. -1 if not defined yet.
            buffer_type (BufferType | None): Type of the output Buffer. None if not defined yet.
        """
        self.__links: list[TransformLinkBase] = []
        """Ordered list of links defining the transform."""

        # sanity check
        if buffer_count < 0:
            assert buffer_count == -1, "TransformChain: buffer_count must be -1 (undefined) or >= 0"

        self.__buffer_count = buffer_count
        """Number of elements in the output Buffer. -1 if not defined yet."""

        self.__buffer_type = buffer_type
        """Type of the output Buffer. None if not defined yet."""

    # =============================================================================
    #
    # =============================================================================

    def is_fully_defined(self) -> bool:
        """Check if the TransformChain is fully defined.

        A TransformChain is fully defined when both buffer_type is not None
        and buffer_count is >= 0.

        Returns:
            bool: True if fully defined, False otherwise.
        """
        if self.__buffer_type is None:
            return False
        if self.__buffer_count < 0:
            return False
        return True

    def get_buffer_count(self) -> int:
        """Get the number of elements in the output Buffer.

        Note:
            This method should only be called if is_fully_defined() returns True.

        Returns:
            int: The number of elements in the output Buffer.

        Raises:
            AssertionError: If buffer_type is None or buffer_count is negative.
        """
        # sanity check - buffer_count MUST be defined
        assert self.__buffer_type is not None, "TransformChain.get_buffer_count: buffer_type is None. use .is_fully_defined() to check."
        assert self.__buffer_count >= 0, "TransformChain.get_buffer_count: buffer_count is negative. use .is_fully_defined() to check."

        # return the buffer count
        return self.__buffer_count

    def get_buffer_type(self) -> BufferType:
        """Get the type of the output Buffer.

        Note:
            This method should only be called if is_fully_defined() returns True.

        Returns:
            BufferType: The type of the output Buffer.

        Raises:
            AssertionError: If buffer_type is None or buffer_count is negative.
        """
        # sanity check - buffer_type MUST be defined
        assert self.__buffer_type is not None, "TransformChain.get_buffer_type: buffer_type is None. use .is_fully_defined() to check."
        assert self.__buffer_count >= 0, "TransformChain.get_buffer_count: buffer_count is negative. use .is_fully_defined() to check."

        # return the buffer type
        return self.__buffer_type

    # =============================================================================
    # .add/.remove/.clear the links
    # =============================================================================

    def add(self, link: TransformLinkBase) -> None:
        """Add a TransformLink to the chain.

        Args:
            link: The TransformLink to add to the chain.
        """
        self.__links.append(link)

    def remove(self, link: TransformLinkBase) -> None:
        """Remove a TransformLink from the chain.

        Args:
            link: The TransformLink to remove from the chain.

        Raises:
            ValueError: If the link is not found in the chain.
        """
        self.__links.remove(link)

    # =============================================================================
    # .run()
    # =============================================================================

    def run(self) -> Buffer:
        """Compute the transform and return a Buffer with the result.

        Applies each link in the chain sequentially, passing the output of
        each link to the next link in the chain.

        Returns:
            Buffer: The final transformed buffer.

        Raises:
            AssertionError: If no buffer is produced by the transform chain.
        """
        # Create a new Buffer to hold the transformed data
        buffer = None

        # Apply each link in the chain
        for link in self.__links:
            buffer = link.apply(buffer)

        # Sanity check the output buffer
        assert buffer is not None, "TransformChain.to_buffer: No buffer produced by the transform chain."

        # Return the final buffer
        return buffer

    # =============================================================================
    # Serialisation
    # =============================================================================

    def serialize(self) -> dict[str, Any]:
        """Serialize the TransformChain to a dictionary.

        Returns:
            dict[str, Any]: The serialized TransformChain containing buffer_count,
                buffer_type, and links.
        """
        links_data = [link.serialize() for link in self.__links]
        chain_serialized = {
            "buffer_count": self.__buffer_count,
            "buffer_type": self.__buffer_type.name if self.__buffer_type is not None else None,
            "links": links_data,
        }
        return chain_serialized

    @staticmethod
    def deserialize(data: dict[str, Any]) -> "TransformChain":
        """Deserialize a TransformChain from a dictionary.

        Args:
            data (dict[str, Any]): The serialized TransformChain.

        Returns:
            TransformChain: The deserialized TransformChain instance.
        """
        buffer_count = int(data["buffer_count"])
        buffer_type_str: str | None = data["buffer_type"]
        buffer_type = BufferType[buffer_type_str] if buffer_type_str is not None else None

        transform_chain = TransformChain(buffer_count, buffer_type)

        links_data: list[dict[str, Any]] = data["links"]
        for link_data in links_data:
            link_type: str = link_data["link_type"]
            link_class: type[TransformLinkBase] = TransformRegistry.get_link_class(link_type)
            link_instance = link_class.deserialize(link_data)
            transform_chain.add(link_instance)

        return transform_chain

__buffer_count = buffer_count instance-attribute

Number of elements in the output Buffer. -1 if not defined yet.

__buffer_type = buffer_type instance-attribute

Type of the output Buffer. None if not defined yet.

Ordered list of links defining the transform.

__init__(buffer_count: int, buffer_type: BufferType | None) -> None

Initialize a TransformChain.

Parameters:

Name Type Description Default
buffer_count int

Number of elements in the output Buffer. -1 if not defined yet.

required
buffer_type gsp.types.BufferType | None

Type of the output Buffer. None if not defined yet.

required
Source code in src/gsp/transforms/transform_chain.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def __init__(self, buffer_count: int, buffer_type: BufferType | None) -> None:
    """Initialize a TransformChain.

    Args:
        buffer_count (int): Number of elements in the output Buffer. -1 if not defined yet.
        buffer_type (BufferType | None): Type of the output Buffer. None if not defined yet.
    """
    self.__links: list[TransformLinkBase] = []
    """Ordered list of links defining the transform."""

    # sanity check
    if buffer_count < 0:
        assert buffer_count == -1, "TransformChain: buffer_count must be -1 (undefined) or >= 0"

    self.__buffer_count = buffer_count
    """Number of elements in the output Buffer. -1 if not defined yet."""

    self.__buffer_type = buffer_type
    """Type of the output Buffer. None if not defined yet."""

add(link: TransformLinkBase) -> None

Add a TransformLink to the chain.

Parameters:

Name Type Description Default
link gsp.transforms.transform_link_base.TransformLinkBase

The TransformLink to add to the chain.

required
Source code in src/gsp/transforms/transform_chain.py
103
104
105
106
107
108
109
def add(self, link: TransformLinkBase) -> None:
    """Add a TransformLink to the chain.

    Args:
        link: The TransformLink to add to the chain.
    """
    self.__links.append(link)

deserialize(data: dict[str, Any]) -> TransformChain staticmethod

Deserialize a TransformChain from a dictionary.

Parameters:

Name Type Description Default
data dict[str, typing.Any]

The serialized TransformChain.

required

Returns:

Name Type Description
TransformChain gsp.transforms.transform_chain.TransformChain

The deserialized TransformChain instance.

Source code in src/gsp/transforms/transform_chain.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
@staticmethod
def deserialize(data: dict[str, Any]) -> "TransformChain":
    """Deserialize a TransformChain from a dictionary.

    Args:
        data (dict[str, Any]): The serialized TransformChain.

    Returns:
        TransformChain: The deserialized TransformChain instance.
    """
    buffer_count = int(data["buffer_count"])
    buffer_type_str: str | None = data["buffer_type"]
    buffer_type = BufferType[buffer_type_str] if buffer_type_str is not None else None

    transform_chain = TransformChain(buffer_count, buffer_type)

    links_data: list[dict[str, Any]] = data["links"]
    for link_data in links_data:
        link_type: str = link_data["link_type"]
        link_class: type[TransformLinkBase] = TransformRegistry.get_link_class(link_type)
        link_instance = link_class.deserialize(link_data)
        transform_chain.add(link_instance)

    return transform_chain

get_buffer_count() -> int

Get the number of elements in the output Buffer.

Note

This method should only be called if is_fully_defined() returns True.

Returns:

Name Type Description
int int

The number of elements in the output Buffer.

Raises:

Type Description
AssertionError

If buffer_type is None or buffer_count is negative.

Source code in src/gsp/transforms/transform_chain.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def get_buffer_count(self) -> int:
    """Get the number of elements in the output Buffer.

    Note:
        This method should only be called if is_fully_defined() returns True.

    Returns:
        int: The number of elements in the output Buffer.

    Raises:
        AssertionError: If buffer_type is None or buffer_count is negative.
    """
    # sanity check - buffer_count MUST be defined
    assert self.__buffer_type is not None, "TransformChain.get_buffer_count: buffer_type is None. use .is_fully_defined() to check."
    assert self.__buffer_count >= 0, "TransformChain.get_buffer_count: buffer_count is negative. use .is_fully_defined() to check."

    # return the buffer count
    return self.__buffer_count

get_buffer_type() -> BufferType

Get the type of the output Buffer.

Note

This method should only be called if is_fully_defined() returns True.

Returns:

Name Type Description
BufferType gsp.types.BufferType

The type of the output Buffer.

Raises:

Type Description
AssertionError

If buffer_type is None or buffer_count is negative.

Source code in src/gsp/transforms/transform_chain.py
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def get_buffer_type(self) -> BufferType:
    """Get the type of the output Buffer.

    Note:
        This method should only be called if is_fully_defined() returns True.

    Returns:
        BufferType: The type of the output Buffer.

    Raises:
        AssertionError: If buffer_type is None or buffer_count is negative.
    """
    # sanity check - buffer_type MUST be defined
    assert self.__buffer_type is not None, "TransformChain.get_buffer_type: buffer_type is None. use .is_fully_defined() to check."
    assert self.__buffer_count >= 0, "TransformChain.get_buffer_count: buffer_count is negative. use .is_fully_defined() to check."

    # return the buffer type
    return self.__buffer_type

is_fully_defined() -> bool

Check if the TransformChain is fully defined.

A TransformChain is fully defined when both buffer_type is not None and buffer_count is >= 0.

Returns:

Name Type Description
bool bool

True if fully defined, False otherwise.

Source code in src/gsp/transforms/transform_chain.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def is_fully_defined(self) -> bool:
    """Check if the TransformChain is fully defined.

    A TransformChain is fully defined when both buffer_type is not None
    and buffer_count is >= 0.

    Returns:
        bool: True if fully defined, False otherwise.
    """
    if self.__buffer_type is None:
        return False
    if self.__buffer_count < 0:
        return False
    return True

remove(link: TransformLinkBase) -> None

Remove a TransformLink from the chain.

Parameters:

Name Type Description Default
link gsp.transforms.transform_link_base.TransformLinkBase

The TransformLink to remove from the chain.

required

Raises:

Type Description
ValueError

If the link is not found in the chain.

Source code in src/gsp/transforms/transform_chain.py
111
112
113
114
115
116
117
118
119
120
def remove(self, link: TransformLinkBase) -> None:
    """Remove a TransformLink from the chain.

    Args:
        link: The TransformLink to remove from the chain.

    Raises:
        ValueError: If the link is not found in the chain.
    """
    self.__links.remove(link)

run() -> Buffer

Compute the transform and return a Buffer with the result.

Applies each link in the chain sequentially, passing the output of each link to the next link in the chain.

Returns:

Name Type Description
Buffer gsp.types.Buffer

The final transformed buffer.

Raises:

Type Description
AssertionError

If no buffer is produced by the transform chain.

Source code in src/gsp/transforms/transform_chain.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def run(self) -> Buffer:
    """Compute the transform and return a Buffer with the result.

    Applies each link in the chain sequentially, passing the output of
    each link to the next link in the chain.

    Returns:
        Buffer: The final transformed buffer.

    Raises:
        AssertionError: If no buffer is produced by the transform chain.
    """
    # Create a new Buffer to hold the transformed data
    buffer = None

    # Apply each link in the chain
    for link in self.__links:
        buffer = link.apply(buffer)

    # Sanity check the output buffer
    assert buffer is not None, "TransformChain.to_buffer: No buffer produced by the transform chain."

    # Return the final buffer
    return buffer

serialize() -> dict[str, Any]

Serialize the TransformChain to a dictionary.

Returns:

Type Description
dict[str, typing.Any]

dict[str, Any]: The serialized TransformChain containing buffer_count, buffer_type, and links.

Source code in src/gsp/transforms/transform_chain.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def serialize(self) -> dict[str, Any]:
    """Serialize the TransformChain to a dictionary.

    Returns:
        dict[str, Any]: The serialized TransformChain containing buffer_count,
            buffer_type, and links.
    """
    links_data = [link.serialize() for link in self.__links]
    chain_serialized = {
        "buffer_count": self.__buffer_count,
        "buffer_type": self.__buffer_type.name if self.__buffer_type is not None else None,
        "links": links_data,
    }
    return chain_serialized

Transform Link Base Module.

Bases: abc.ABC

Base class for a link in a Transform chain.

Source code in src/gsp/transforms/transform_link_base.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class TransformLinkBase(ABC):
    """Base class for a link in a Transform chain."""

    @abstractmethod
    def apply(self, buffer_src: Buffer | None) -> Buffer:
        """Apply the transformation to the given buffer and return a new buffer.

        Args:
            buffer_src (Buffer | None): The source buffer to transform. Can be None.

        Returns:
            Buffer: The transformed buffer.
        """
        pass

    @abstractmethod
    def serialize(self) -> dict[str, Any]:
        """Serialize the TransformLink to a dictionary.

        Returns:
            dict[str, Any]: The serialized TransformLink.
        """
        pass

    @staticmethod
    @abstractmethod
    def deserialize(data: dict[str, Any]) -> "TransformLinkBase":
        """Deserialize a TransformLink from a dictionary.

        Args:
            data (dict[str, Any]): The serialized TransformLink.

        Returns:
            TransformLinkBase: The deserialized TransformLink instance.
        """
        pass

Apply the transformation to the given buffer and return a new buffer.

Parameters:

Name Type Description Default
buffer_src gsp.types.Buffer | None

The source buffer to transform. Can be None.

required

Returns:

Name Type Description
Buffer gsp.types.Buffer

The transformed buffer.

Source code in src/gsp/transforms/transform_link_base.py
16
17
18
19
20
21
22
23
24
25
26
@abstractmethod
def apply(self, buffer_src: Buffer | None) -> Buffer:
    """Apply the transformation to the given buffer and return a new buffer.

    Args:
        buffer_src (Buffer | None): The source buffer to transform. Can be None.

    Returns:
        Buffer: The transformed buffer.
    """
    pass

Deserialize a TransformLink from a dictionary.

Parameters:

Name Type Description Default
data dict[str, typing.Any]

The serialized TransformLink.

required

Returns:

Name Type Description
TransformLinkBase gsp.transforms.transform_link_base.TransformLinkBase

The deserialized TransformLink instance.

Source code in src/gsp/transforms/transform_link_base.py
37
38
39
40
41
42
43
44
45
46
47
48
@staticmethod
@abstractmethod
def deserialize(data: dict[str, Any]) -> "TransformLinkBase":
    """Deserialize a TransformLink from a dictionary.

    Args:
        data (dict[str, Any]): The serialized TransformLink.

    Returns:
        TransformLinkBase: The deserialized TransformLink instance.
    """
    pass

Serialize the TransformLink to a dictionary.

Returns:

Type Description
dict[str, typing.Any]

dict[str, Any]: The serialized TransformLink.

Source code in src/gsp/transforms/transform_link_base.py
28
29
30
31
32
33
34
35
@abstractmethod
def serialize(self) -> dict[str, Any]:
    """Serialize the TransformLink to a dictionary.

    Returns:
        dict[str, Any]: The serialized TransformLink.
    """
    pass

Utilities Module

gsp.utils

Utility modules for GSP.

gsp.utils.cmap_utils

Colormap utilities for mapping values to colors using matplotlib colormaps.

CmapUtils

Utility class for colormap operations. Leverage matplotlib colormaps.

Source code in src/gsp/utils/cmap_utils.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class CmapUtils:
    """Utility class for colormap operations. Leverage [matplotlib colormaps](https://matplotlib.org/stable/tutorials/colors/colormaps.html)."""

    @staticmethod
    def has_color_map(colormap_name: str) -> bool:
        """Check if the given colormap name is recognized by matplotlib.

        Args:
            colormap_name (str): Name of the colormap to check.

        Returns:
            bool: True if the colormap exists, False otherwise.
        """
        try:
            matplotlib.cm.get_cmap(colormap_name)
            return True
        except ValueError:
            return False

    @staticmethod
    def get_color_map(colormap_name: str, values: np.ndarray, vmin: float | None = None, vmax: float | None = None) -> Buffer:
        """Get colors from a colormap for the given values.

        Args:
            colormap_name (str): Name of the colormap (e.g., 'plasma', 'viridis', etc.).
            values (np.ndarray): Array of input values to map to colors.
            vmin (float|None): Minimum value for values normalization. if None, use min of values.
            vmax (float|None): Maximum value for values normalization. if None, use max of values.

        Returns:
            Buffer: A Buffer containing the RGBA8 colors mapped from the input values.
        """
        # Handle default parameters
        vmin = vmin if vmin is not None else values.min()
        vmax = vmax if vmax is not None else values.max()

        # sanity check
        assert CmapUtils.has_color_map(colormap_name), f"Colormap '{colormap_name}' is not recognized."

        mpl_color_map = matplotlib.cm.get_cmap(colormap_name)

        # sanity check
        assert vmin is not None, "vmin should not be None"
        assert vmax is not None, "vmax should not be None"

        normalized_values = (values - vmin) / (vmax - vmin)
        normalized_values = np.clip(normalized_values, 0.0, 1.0)
        color_mapped_normalized = mpl_color_map(normalized_values)  # normalized values to [0, 1]
        color_mapped_255 = (color_mapped_normalized * 255).astype(np.uint8)

        # Create a Buffer
        color_buffer = Buffer(color_mapped_255.__len__(), buffer_type=BufferType.rgba8)
        color_buffer.set_data(bytearray(color_mapped_255.tobytes()), 0, color_mapped_255.__len__())

        # Return the color buffer
        return color_buffer

get_color_map(colormap_name: str, values: np.ndarray, vmin: float | None = None, vmax: float | None = None) -> Buffer staticmethod

Get colors from a colormap for the given values.

Parameters:

Name Type Description Default
colormap_name str

Name of the colormap (e.g., 'plasma', 'viridis', etc.).

required
values numpy.ndarray

Array of input values to map to colors.

required
vmin float | None

Minimum value for values normalization. if None, use min of values.

None
vmax float | None

Maximum value for values normalization. if None, use max of values.

None

Returns:

Name Type Description
Buffer gsp.types.Buffer

A Buffer containing the RGBA8 colors mapped from the input values.

Source code in src/gsp/utils/cmap_utils.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@staticmethod
def get_color_map(colormap_name: str, values: np.ndarray, vmin: float | None = None, vmax: float | None = None) -> Buffer:
    """Get colors from a colormap for the given values.

    Args:
        colormap_name (str): Name of the colormap (e.g., 'plasma', 'viridis', etc.).
        values (np.ndarray): Array of input values to map to colors.
        vmin (float|None): Minimum value for values normalization. if None, use min of values.
        vmax (float|None): Maximum value for values normalization. if None, use max of values.

    Returns:
        Buffer: A Buffer containing the RGBA8 colors mapped from the input values.
    """
    # Handle default parameters
    vmin = vmin if vmin is not None else values.min()
    vmax = vmax if vmax is not None else values.max()

    # sanity check
    assert CmapUtils.has_color_map(colormap_name), f"Colormap '{colormap_name}' is not recognized."

    mpl_color_map = matplotlib.cm.get_cmap(colormap_name)

    # sanity check
    assert vmin is not None, "vmin should not be None"
    assert vmax is not None, "vmax should not be None"

    normalized_values = (values - vmin) / (vmax - vmin)
    normalized_values = np.clip(normalized_values, 0.0, 1.0)
    color_mapped_normalized = mpl_color_map(normalized_values)  # normalized values to [0, 1]
    color_mapped_255 = (color_mapped_normalized * 255).astype(np.uint8)

    # Create a Buffer
    color_buffer = Buffer(color_mapped_255.__len__(), buffer_type=BufferType.rgba8)
    color_buffer.set_data(bytearray(color_mapped_255.tobytes()), 0, color_mapped_255.__len__())

    # Return the color buffer
    return color_buffer

has_color_map(colormap_name: str) -> bool staticmethod

Check if the given colormap name is recognized by matplotlib.

Parameters:

Name Type Description Default
colormap_name str

Name of the colormap to check.

required

Returns:

Name Type Description
bool bool

True if the colormap exists, False otherwise.

Source code in src/gsp/utils/cmap_utils.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@staticmethod
def has_color_map(colormap_name: str) -> bool:
    """Check if the given colormap name is recognized by matplotlib.

    Args:
        colormap_name (str): Name of the colormap to check.

    Returns:
        bool: True if the colormap exists, False otherwise.
    """
    try:
        matplotlib.cm.get_cmap(colormap_name)
        return True
    except ValueError:
        return False

gsp.utils.group_utils

Utility functions for handling and manipulating group objects.

This module provides utilities for working with Groups, which can be represented in multiple formats: int, list[int], or list[list[int]].

GroupUtils

Utility class for group operations and validation.

This class provides static methods for: - Validating group objects - Computing group counts and indices - Converting between different group formats

Source code in src/gsp/utils/group_utils.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
class GroupUtils:
    """Utility class for group operations and validation.

    This class provides static methods for:
    - Validating group objects
    - Computing group counts and indices
    - Converting between different group formats
    """

    @staticmethod
    def get_group_count(vertex_count: int, groups: Groups) -> int:
        """Return the number of groups from the groups object.

        Args:
            vertex_count (int): number of vertices
            groups (Groups): groups object

        Returns:
            int: number of groups

        Raises:
            NotImplementedError: if the groups object type is not supported
        """
        groups_format = GroupUtils._groups_format(groups)
        if groups_format == GroupUtils.FORMAT_INT:
            groups_typed = typing.cast(int, groups)
            group_count = vertex_count // groups_typed
        elif groups_format == GroupUtils.FORMAT_LIST_INT:
            groups_typed = typing.cast(list[int], groups)
            group_count = len(groups_typed)
        elif groups_format == GroupUtils.FORMAT_LIST_LIST_INT:
            groups_typed = typing.cast(list[list[int]], groups)
            group_count = len(groups_typed)
        else:
            raise NotImplementedError(f"Group buffer shape not supported: {type(groups)}")

        return group_count

    # =============================================================================
    # is_instance_of_groups
    # =============================================================================

    @staticmethod
    def is_instance_of_groups(groups: Groups) -> bool:
        """Check if the type of groups is valid.

        Deep check where all elements are checked.
        Does not check the values themselves, only the types.

        Groups can be:
        - int
        - list[int]
        - list[list[int]]

        Args:
            groups (Groups): groups object

        Returns:
            bool: True if groups is a valid Groups object, False otherwise.
        """
        if isinstance(groups, int):
            return True
        elif isinstance(groups, list) and all(isinstance(int_value, int) for int_value in groups):
            return True
        elif isinstance(groups, list) and all(isinstance(group, list) for group in groups) and all(isinstance(int_value, int) for int_list in groups for int_value in int_list):  # type: ignore[union-attr]
            return True
        else:
            return False

    # =============================================================================
    # ._groups_format()
    # =============================================================================

    FORMAT_INT = "format_int"
    FORMAT_LIST_INT = "format_list_int"
    FORMAT_LIST_LIST_INT = "format_list_list_int"

    @staticmethod
    def _groups_format(groups: Groups) -> Literal["format_int", "format_list_int", "format_list_list_int"]:
        """Return the format of the groups object as a string.

        No check is done.

        Args:
            groups (Groups): groups object

        Returns:
            Literal["format_int", "format_list_int", "format_list_list_int"]: "format_int", "format_list_int", "format_list_list_int"

        Raises:
            ValueError: if the groups object is not valid
        """
        if isinstance(groups, int):
            return GroupUtils.FORMAT_INT
        elif isinstance(groups, list) and groups.__len__() > 0 and isinstance(groups[0], int):
            return GroupUtils.FORMAT_LIST_INT
        elif isinstance(groups, list) and groups.__len__() > 0 and isinstance(groups[0], list) and groups[0].__len__() > 0 and isinstance(groups[0][0], int):
            return GroupUtils.FORMAT_LIST_LIST_INT
        else:
            raise ValueError(f"Groups object is not valid: {type(groups)}")

    # =============================================================================
    # Sanity Checks
    # =============================================================================

    @staticmethod
    def sanity_check(vertex_count: int, groups: Groups) -> None:
        """Perform sanity checks on the groups object, raising exceptions if not valid.

        Raise:
            ValueError: if the groups object is not valid.
        """
        if not GroupUtils.is_instance_of_groups(groups):
            raise ValueError(f"Groups object is not valid: {type(groups)}")

        groups_mode = GroupUtils._groups_format(groups)

        if groups_mode == GroupUtils.FORMAT_INT:
            groups_int = typing.cast(int, groups)
            if groups_int <= 0:
                raise ValueError(f"Groups as int must be positive, got {groups_int}")
            if groups_int > vertex_count:
                raise ValueError(f"Groups as int must be less than or equal to vertex_count, got groups={groups_int}, vertex_count={vertex_count}")
            if vertex_count % groups_int != 0:
                raise ValueError(f"Groups as int must divide vertex_count, got vertex_count={vertex_count}, groups={groups_int}")
        elif groups_mode == GroupUtils.FORMAT_LIST_INT:
            groups_list_int = typing.cast(list[int], groups)
            if any(group_size <= 0 for group_size in groups_list_int):
                raise ValueError(f"Groups as list[int], group sizes must be all positive, got {groups_list_int}")
            if sum(groups_list_int) != vertex_count:
                raise ValueError(
                    f"Sum of groups size as list[int] must equal vertex_count, got sum(groups)={sum(groups_list_int)}, vertex_count={vertex_count}"
                )
        elif groups_mode == GroupUtils.FORMAT_LIST_LIST_INT:
            groups_list_list_int = typing.cast(list[list[int]], groups)
            all_indices = [index for group in groups_list_list_int for index in group]
            if any(index < 0 or index >= vertex_count for index in all_indices):
                raise ValueError(f"Groups as list[list[int]], all indices must be in range [0, {vertex_count-1}], got indices={all_indices}")
            if len(set(all_indices)) != len(all_indices):
                raise ValueError(f"Groups as list[list[int]], all indices must be unique, got indices={all_indices}")

        # TODO check that the list matches the vertex count where needed
        # TODO check that no list is empty

    @staticmethod
    def sanity_check_safe(vertex_count: int, groups: Groups) -> bool:
        """Perform sanity checks on the groups object.

        Same as .sanity_check_groups() but does not raise exceptions if not valid.

        Args:
            vertex_count (int): number of vertices
            groups (Groups): groups object

        Returns:
            bool: True if the groups object is valid, False otherwise.
        """
        try:
            GroupUtils.sanity_check(vertex_count, groups)
            return True
        except ValueError:
            return False

    # =============================================================================
    # .compute_indices_per_group
    # =============================================================================

    @staticmethod
    def compute_indices_per_group(vertex_count: int, groups: Groups) -> list[list[int]]:
        """Compute indices_per_group for groups depending on the type of groups.

        Args:
            vertex_count (int): number of vertices
            groups (Groups): groups object

        Returns:
            list[list[int]]: list of vertex indices per group

        Raises:
            NotImplementedError: if the groups object type is not supported
        """
        # sanity check
        assert GroupUtils.sanity_check_safe(vertex_count, groups), "groups failed sanity check"

        groups_format = GroupUtils._groups_format(groups)
        if groups_format == GroupUtils.FORMAT_INT:
            groups_typed = typing.cast(int, groups)
            indices_per_group = GroupUtils._compute_indices_per_group_int(vertex_count, groups_typed)
        elif groups_format == GroupUtils.FORMAT_LIST_INT:
            groups_typed = typing.cast(list[int], groups)
            indices_per_group = GroupUtils._compute_indices_per_group_list_int(vertex_count, groups_typed)
        elif groups_format == GroupUtils.FORMAT_LIST_LIST_INT:
            groups_typed = typing.cast(list[list[int]], groups)
            indices_per_group = GroupUtils._compute_indices_per_group_list_list_int(vertex_count, groups_typed)
        else:
            raise NotImplementedError(f"Group buffer shape not supported: {type(groups)}")

        return indices_per_group

    # =============================================================================
    # ._compute_indices_per_group_*() for each format
    # =============================================================================

    @staticmethod
    def _compute_indices_per_group_int(vertex_count: int, groups: int) -> list[list[int]]:
        """Compute indices_per_group for groups as int.

        The int represents the size of each group.

        group_count = vertex_count // groups
        indices_per_group = list[list[int]]

        Examples:
        - vertex_count = 6, groups = 2 - divisible - all groups are vertex_count // groups long
          - indices_per_group = [[0, 1], [2, 3], [4, 5]]

        Args:
            vertex_count (int): number of vertices
            groups (int): size of each group

        Returns:
            list[list[int]]: list of vertex indices per group
        """
        # Initialize output variables
        group_count: int = vertex_count // groups
        indices_per_group: list[list[int]] = []

        # Create the indices per group for this case
        element_count_per_group = groups

        for group_index in range(group_count):
            start_index = element_count_per_group * group_index
            end_index = element_count_per_group * (group_index + 1)

            # Fill the indices for this group
            indices_per_group.append(list(range(start_index, end_index)))

        return indices_per_group

    @staticmethod
    def _compute_indices_per_group_list_int(vertex_count: int, groups: list[int]) -> list[list[int]]:
        """Compute indices_per_group for groups as list[int].

        In this case, each int represents the size of each group.

        group_count = len(groups)
        indices_per_group = list[list[int]]

        Args:
            vertex_count (int): number of vertices
            groups (list[int]): list of group sizes

        Returns:
            list[list[int]]: list of vertex indices per group
        """
        # Initialize output variables
        indices_per_group: list[list[int]] = []

        # Create the indices per group for this case
        current_index = 0
        for group_size in groups:
            # Fill the indices for this group
            group_indices = list(range(current_index, current_index + group_size))
            indices_per_group.append(group_indices)

            # Update the current index
            current_index += group_size

        return indices_per_group

    @staticmethod
    def _compute_indices_per_group_list_list_int(vertex_count: int, groups: list[list[int]]) -> list[list[int]]:
        """Compute indices_per_group for groups as list[list[int]].

        In this case, the groups are directly the indices per group themselves.

        group_count = len(groups)
        indices_per_group = list[list[int]]

        Args:
            vertex_count (int): number of vertices
            groups (list[list[int]]): list of vertex indices per group

        Returns:
            list[list[int]]: list of vertex indices per group
        """
        # Initialize output variables
        indices_per_group: list[list[int]] = groups

        return indices_per_group

compute_indices_per_group(vertex_count: int, groups: Groups) -> list[list[int]] staticmethod

Compute indices_per_group for groups depending on the type of groups.

Parameters:

Name Type Description Default
vertex_count int

number of vertices

required
groups gsp.types.group.Groups

groups object

required

Returns:

Type Description
list[list[int]]

list[list[int]]: list of vertex indices per group

Raises:

Type Description
NotImplementedError

if the groups object type is not supported

Source code in src/gsp/utils/group_utils.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
@staticmethod
def compute_indices_per_group(vertex_count: int, groups: Groups) -> list[list[int]]:
    """Compute indices_per_group for groups depending on the type of groups.

    Args:
        vertex_count (int): number of vertices
        groups (Groups): groups object

    Returns:
        list[list[int]]: list of vertex indices per group

    Raises:
        NotImplementedError: if the groups object type is not supported
    """
    # sanity check
    assert GroupUtils.sanity_check_safe(vertex_count, groups), "groups failed sanity check"

    groups_format = GroupUtils._groups_format(groups)
    if groups_format == GroupUtils.FORMAT_INT:
        groups_typed = typing.cast(int, groups)
        indices_per_group = GroupUtils._compute_indices_per_group_int(vertex_count, groups_typed)
    elif groups_format == GroupUtils.FORMAT_LIST_INT:
        groups_typed = typing.cast(list[int], groups)
        indices_per_group = GroupUtils._compute_indices_per_group_list_int(vertex_count, groups_typed)
    elif groups_format == GroupUtils.FORMAT_LIST_LIST_INT:
        groups_typed = typing.cast(list[list[int]], groups)
        indices_per_group = GroupUtils._compute_indices_per_group_list_list_int(vertex_count, groups_typed)
    else:
        raise NotImplementedError(f"Group buffer shape not supported: {type(groups)}")

    return indices_per_group

get_group_count(vertex_count: int, groups: Groups) -> int staticmethod

Return the number of groups from the groups object.

Parameters:

Name Type Description Default
vertex_count int

number of vertices

required
groups gsp.types.group.Groups

groups object

required

Returns:

Name Type Description
int int

number of groups

Raises:

Type Description
NotImplementedError

if the groups object type is not supported

Source code in src/gsp/utils/group_utils.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@staticmethod
def get_group_count(vertex_count: int, groups: Groups) -> int:
    """Return the number of groups from the groups object.

    Args:
        vertex_count (int): number of vertices
        groups (Groups): groups object

    Returns:
        int: number of groups

    Raises:
        NotImplementedError: if the groups object type is not supported
    """
    groups_format = GroupUtils._groups_format(groups)
    if groups_format == GroupUtils.FORMAT_INT:
        groups_typed = typing.cast(int, groups)
        group_count = vertex_count // groups_typed
    elif groups_format == GroupUtils.FORMAT_LIST_INT:
        groups_typed = typing.cast(list[int], groups)
        group_count = len(groups_typed)
    elif groups_format == GroupUtils.FORMAT_LIST_LIST_INT:
        groups_typed = typing.cast(list[list[int]], groups)
        group_count = len(groups_typed)
    else:
        raise NotImplementedError(f"Group buffer shape not supported: {type(groups)}")

    return group_count

is_instance_of_groups(groups: Groups) -> bool staticmethod

Check if the type of groups is valid.

Deep check where all elements are checked. Does not check the values themselves, only the types.

Groups can be: - int - list[int] - list[list[int]]

Parameters:

Name Type Description Default
groups gsp.types.group.Groups

groups object

required

Returns:

Name Type Description
bool bool

True if groups is a valid Groups object, False otherwise.

Source code in src/gsp/utils/group_utils.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@staticmethod
def is_instance_of_groups(groups: Groups) -> bool:
    """Check if the type of groups is valid.

    Deep check where all elements are checked.
    Does not check the values themselves, only the types.

    Groups can be:
    - int
    - list[int]
    - list[list[int]]

    Args:
        groups (Groups): groups object

    Returns:
        bool: True if groups is a valid Groups object, False otherwise.
    """
    if isinstance(groups, int):
        return True
    elif isinstance(groups, list) and all(isinstance(int_value, int) for int_value in groups):
        return True
    elif isinstance(groups, list) and all(isinstance(group, list) for group in groups) and all(isinstance(int_value, int) for int_list in groups for int_value in int_list):  # type: ignore[union-attr]
        return True
    else:
        return False

sanity_check(vertex_count: int, groups: Groups) -> None staticmethod

Perform sanity checks on the groups object, raising exceptions if not valid.

Raise

ValueError: if the groups object is not valid.

Source code in src/gsp/utils/group_utils.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@staticmethod
def sanity_check(vertex_count: int, groups: Groups) -> None:
    """Perform sanity checks on the groups object, raising exceptions if not valid.

    Raise:
        ValueError: if the groups object is not valid.
    """
    if not GroupUtils.is_instance_of_groups(groups):
        raise ValueError(f"Groups object is not valid: {type(groups)}")

    groups_mode = GroupUtils._groups_format(groups)

    if groups_mode == GroupUtils.FORMAT_INT:
        groups_int = typing.cast(int, groups)
        if groups_int <= 0:
            raise ValueError(f"Groups as int must be positive, got {groups_int}")
        if groups_int > vertex_count:
            raise ValueError(f"Groups as int must be less than or equal to vertex_count, got groups={groups_int}, vertex_count={vertex_count}")
        if vertex_count % groups_int != 0:
            raise ValueError(f"Groups as int must divide vertex_count, got vertex_count={vertex_count}, groups={groups_int}")
    elif groups_mode == GroupUtils.FORMAT_LIST_INT:
        groups_list_int = typing.cast(list[int], groups)
        if any(group_size <= 0 for group_size in groups_list_int):
            raise ValueError(f"Groups as list[int], group sizes must be all positive, got {groups_list_int}")
        if sum(groups_list_int) != vertex_count:
            raise ValueError(
                f"Sum of groups size as list[int] must equal vertex_count, got sum(groups)={sum(groups_list_int)}, vertex_count={vertex_count}"
            )
    elif groups_mode == GroupUtils.FORMAT_LIST_LIST_INT:
        groups_list_list_int = typing.cast(list[list[int]], groups)
        all_indices = [index for group in groups_list_list_int for index in group]
        if any(index < 0 or index >= vertex_count for index in all_indices):
            raise ValueError(f"Groups as list[list[int]], all indices must be in range [0, {vertex_count-1}], got indices={all_indices}")
        if len(set(all_indices)) != len(all_indices):
            raise ValueError(f"Groups as list[list[int]], all indices must be unique, got indices={all_indices}")

sanity_check_safe(vertex_count: int, groups: Groups) -> bool staticmethod

Perform sanity checks on the groups object.

Same as .sanity_check_groups() but does not raise exceptions if not valid.

Parameters:

Name Type Description Default
vertex_count int

number of vertices

required
groups gsp.types.group.Groups

groups object

required

Returns:

Name Type Description
bool bool

True if the groups object is valid, False otherwise.

Source code in src/gsp/utils/group_utils.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
@staticmethod
def sanity_check_safe(vertex_count: int, groups: Groups) -> bool:
    """Perform sanity checks on the groups object.

    Same as .sanity_check_groups() but does not raise exceptions if not valid.

    Args:
        vertex_count (int): number of vertices
        groups (Groups): groups object

    Returns:
        bool: True if the groups object is valid, False otherwise.
    """
    try:
        GroupUtils.sanity_check(vertex_count, groups)
        return True
    except ValueError:
        return False

gsp.utils.math_utils

Mathematical utility functions for GSP.

This module provides mathematical operations and transformations used throughout the GSP library, including: - Model-View-Projection (MVP) transformations for 3D graphics

MathUtils

Utility class for mathematical operations in GSP.

This class provides static methods for common mathematical operations used in graphics programming, such as matrix transformations.

Source code in src/gsp/utils/math_utils.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class MathUtils:
    """Utility class for mathematical operations in GSP.

    This class provides static methods for common mathematical operations
    used in graphics programming, such as matrix transformations.
    """

    @staticmethod
    def apply_mvp_to_vertices(vertices: np.ndarray, model_matrix: np.ndarray, view_matrix: np.ndarray, projection_matrix: np.ndarray) -> np.ndarray:
        """Applies Model-View-Projection transformation to the vertices.

        Args:
            vertices (np.ndarray): Input vertices of shape (N, 3).
            model_matrix (np.ndarray): Model matrix of shape (4, 4).
            view_matrix (np.ndarray): View matrix of shape (4, 4).
            projection_matrix (np.ndarray): Projection matrix of shape (4, 4).

        Returns:
            np.ndarray: Transformed vertices of shape (N, 3).
        """
        # sanity checks
        assert vertices.ndim == 2 and vertices.shape[1] == 3, f"Expected vertices shape (N, 3), got {vertices.shape}"
        assert model_matrix.shape == (4, 4), f"Expected model_matrix shape (4, 4), got {model_matrix.shape}"
        assert view_matrix.shape == (4, 4), f"Expected view_matrix shape (4, 4), got {view_matrix.shape}"
        assert projection_matrix.shape == (4, 4), f"Expected projection_matrix shape (4, 4), got {projection_matrix.shape}"

        # Compute the Model-View-Projection (MVP) matrix
        mvp_matrix = projection_matrix @ view_matrix @ model_matrix

        # convert vertices to homogeneous coordinates (x, y, z) -> (x, y, z, w=1.0)
        ws_column = np.ones((vertices.shape[0], 1), dtype=np.float32)
        vertices_homogeneous = np.hstack((vertices, ws_column))  # shape (N, 4) for N vertices

        # Apply the MVP transformation to the vertices
        vertices_transformed = (mvp_matrix @ vertices_homogeneous.T).T  # shape (N, 4)

        # Perform perspective division to get normalized device coordinates (NDC)
        vertices_homo_transformed = vertices_transformed / vertices_transformed[:, 3:4]  # divide by w - shape (N, 4)
        vertices_3d_transformed = vertices_homo_transformed[:, :3]  # drop w-coordinate - shape (N, 3)

        # NOTE: no need to map NDC to screen coordinates as canvas is drawn directly in NDC coordinates 2d
        pass

        # return the transformed vertices
        return vertices_3d_transformed

apply_mvp_to_vertices(vertices: np.ndarray, model_matrix: np.ndarray, view_matrix: np.ndarray, projection_matrix: np.ndarray) -> np.ndarray staticmethod

Applies Model-View-Projection transformation to the vertices.

Parameters:

Name Type Description Default
vertices numpy.ndarray

Input vertices of shape (N, 3).

required
model_matrix numpy.ndarray

Model matrix of shape (4, 4).

required
view_matrix numpy.ndarray

View matrix of shape (4, 4).

required
projection_matrix numpy.ndarray

Projection matrix of shape (4, 4).

required

Returns:

Type Description
numpy.ndarray

np.ndarray: Transformed vertices of shape (N, 3).

Source code in src/gsp/utils/math_utils.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@staticmethod
def apply_mvp_to_vertices(vertices: np.ndarray, model_matrix: np.ndarray, view_matrix: np.ndarray, projection_matrix: np.ndarray) -> np.ndarray:
    """Applies Model-View-Projection transformation to the vertices.

    Args:
        vertices (np.ndarray): Input vertices of shape (N, 3).
        model_matrix (np.ndarray): Model matrix of shape (4, 4).
        view_matrix (np.ndarray): View matrix of shape (4, 4).
        projection_matrix (np.ndarray): Projection matrix of shape (4, 4).

    Returns:
        np.ndarray: Transformed vertices of shape (N, 3).
    """
    # sanity checks
    assert vertices.ndim == 2 and vertices.shape[1] == 3, f"Expected vertices shape (N, 3), got {vertices.shape}"
    assert model_matrix.shape == (4, 4), f"Expected model_matrix shape (4, 4), got {model_matrix.shape}"
    assert view_matrix.shape == (4, 4), f"Expected view_matrix shape (4, 4), got {view_matrix.shape}"
    assert projection_matrix.shape == (4, 4), f"Expected projection_matrix shape (4, 4), got {projection_matrix.shape}"

    # Compute the Model-View-Projection (MVP) matrix
    mvp_matrix = projection_matrix @ view_matrix @ model_matrix

    # convert vertices to homogeneous coordinates (x, y, z) -> (x, y, z, w=1.0)
    ws_column = np.ones((vertices.shape[0], 1), dtype=np.float32)
    vertices_homogeneous = np.hstack((vertices, ws_column))  # shape (N, 4) for N vertices

    # Apply the MVP transformation to the vertices
    vertices_transformed = (mvp_matrix @ vertices_homogeneous.T).T  # shape (N, 4)

    # Perform perspective division to get normalized device coordinates (NDC)
    vertices_homo_transformed = vertices_transformed / vertices_transformed[:, 3:4]  # divide by w - shape (N, 4)
    vertices_3d_transformed = vertices_homo_transformed[:, :3]  # drop w-coordinate - shape (N, 3)

    # NOTE: no need to map NDC to screen coordinates as canvas is drawn directly in NDC coordinates 2d
    pass

    # return the transformed vertices
    return vertices_3d_transformed

gsp.utils.transbuf_utils

Utility functions for working with TransBuf objects.

This module provides helper functions to convert TransBuf instances to Buffer objects, handling both direct Buffer instances and TransformChain objects.

TransBufUtils

Utility class for TransBuf conversions and operations.

This class provides static methods for converting TransBuf objects to Buffer objects, supporting both direct Buffer instances and TransformChain objects that need to be executed.

Source code in src/gsp/utils/transbuf_utils.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class TransBufUtils:
    """Utility class for TransBuf conversions and operations.

    This class provides static methods for converting TransBuf objects
    to Buffer objects, supporting both direct Buffer instances and
    TransformChain objects that need to be executed.
    """

    @staticmethod
    def to_buffer(trans_buf: TransBuf) -> Buffer:
        """Convert a TransBuf to a Buffer.

        Args:
            trans_buf: A TransBuf object which can be either a Buffer or a TransformChain.

        Returns:
            A Buffer object. If the input is already a Buffer, it's returned directly.
            If it's a TransformChain, it's executed and the resulting Buffer is returned.

        Raises:
            ValueError: If the trans_buf is neither a Buffer nor a TransformChain.
        """
        if isinstance(trans_buf, Buffer):
            buffer = typing.cast(Buffer, trans_buf)
            return buffer
        elif isinstance(trans_buf, TransformChain):
            transform_chain = typing.cast(TransformChain, trans_buf)
            buffer = transform_chain.run()
            return buffer
        else:
            raise ValueError(f"Unsupported type for transbuf_to_buffer {type(trans_buf)}")

to_buffer(trans_buf: TransBuf) -> Buffer staticmethod

Convert a TransBuf to a Buffer.

Parameters:

Name Type Description Default
trans_buf gsp.types.transbuf.TransBuf

A TransBuf object which can be either a Buffer or a TransformChain.

required

Returns:

Type Description
gsp.types.buffer.Buffer

A Buffer object. If the input is already a Buffer, it's returned directly.

gsp.types.buffer.Buffer

If it's a TransformChain, it's executed and the resulting Buffer is returned.

Raises:

Type Description
ValueError

If the trans_buf is neither a Buffer nor a TransformChain.

Source code in src/gsp/utils/transbuf_utils.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@staticmethod
def to_buffer(trans_buf: TransBuf) -> Buffer:
    """Convert a TransBuf to a Buffer.

    Args:
        trans_buf: A TransBuf object which can be either a Buffer or a TransformChain.

    Returns:
        A Buffer object. If the input is already a Buffer, it's returned directly.
        If it's a TransformChain, it's executed and the resulting Buffer is returned.

    Raises:
        ValueError: If the trans_buf is neither a Buffer nor a TransformChain.
    """
    if isinstance(trans_buf, Buffer):
        buffer = typing.cast(Buffer, trans_buf)
        return buffer
    elif isinstance(trans_buf, TransformChain):
        transform_chain = typing.cast(TransformChain, trans_buf)
        buffer = transform_chain.run()
        return buffer
    else:
        raise ValueError(f"Unsupported type for transbuf_to_buffer {type(trans_buf)}")

gsp.utils.unit_utils

"Utility functions for unit conversions related to display and typography.

UnitUtils

Utility class for unit conversions related to display and typography.

Source code in src/gsp/utils/unit_utils.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
class UnitUtils:
    """Utility class for unit conversions related to display and typography."""
    @staticmethod
    def in_to_cm(inches: float) -> float:
        """Convert inches to centimeters.

        Args:
            inches (float): Length in inches.

        Returns:
            float: Length in centimeters.
        """
        return inches * 2.54

    @staticmethod
    def cm_to_in(cm: float) -> float:
        """Convert centimeters to inches.

        Args:
            cm (float): Length in centimeters.

        Returns:
            float: Length in inches.
        """
        return cm / 2.54

    @staticmethod
    def device_pixel_ratio() -> float:
        """Get the device pixel ratio for high-DPI displays.

        Returns:
            float: Device pixel ratio (typically 1.0 for standard displays, >1.0 for high-DPI).
        """
        # detect if running on a macOS retina display or other high-DPI display
        # This is a placeholder implementation; actual detection may vary based on the GUI framework used.
        is_macosx = "darwin" in sys.platform
        if is_macosx:
            return 2.0  # Common value for retina displays
        return 1.0

    @staticmethod
    def pixel_to_point(pixel_size: float, dpi: float) -> float:
        """Convert pixel size to typographic point size based on DPI.

        Args:
            pixel_size (float): Size in pixels.
            dpi (float): Dots per inch of the display.

        Returns:
            float: Size in points.
        """
        inches_per_pixel = 1.0 / dpi
        points_per_inch = 72.0
        point_size = pixel_size * inches_per_pixel * points_per_inch
        return point_size

    @staticmethod
    def point_to_pixel(point_size: float, dpi: float) -> float:
        """Convert point size to pixel size based on DPI.

        Args:
            point_size (float): Size in points.
            dpi (float): Dots per inch of the display.

        Returns:
            float: Size in pixels.
        """
        inches_per_point = 1.0 / 72.0
        pixels_per_inch = dpi
        pixel_size = point_size * inches_per_point * pixels_per_inch
        return pixel_size

    # =============================================================================
    # numpy array versions
    # =============================================================================

    @staticmethod
    def pixel_to_point_numpy(pixel_sizes: np.ndarray, dpi: float) -> np.ndarray:
        """Convert an array of pixel sizes to point sizes based on DPI.

        Args:
            pixel_sizes (np.ndarray): Array of sizes in pixels.
            dpi (float): Dots per inch of the display.

        Returns:
            np.ndarray: Array of sizes in points.
        """
        inches_per_pixel = 1.0 / dpi
        points_per_inch = 72.0
        point_sizes = pixel_sizes * inches_per_pixel * points_per_inch
        return point_sizes

    @staticmethod
    def point_to_pixel_numpy(point_sizes: np.ndarray, dpi: float) -> np.ndarray:
        """Convert an array of point sizes to pixel sizes based on DPI.

        Args:
            point_sizes (np.ndarray): Array of sizes in points.
            dpi (float): Dots per inch of the display.

        Returns:
            np.ndarray: Array of sizes in pixels.
        """
        inches_per_point = 1.0 / 72.0
        pixels_per_inch = dpi
        pixel_sizes = point_sizes * inches_per_point * pixels_per_inch
        return pixel_sizes

cm_to_in(cm: float) -> float staticmethod

Convert centimeters to inches.

Parameters:

Name Type Description Default
cm float

Length in centimeters.

required

Returns:

Name Type Description
float float

Length in inches.

Source code in src/gsp/utils/unit_utils.py
28
29
30
31
32
33
34
35
36
37
38
@staticmethod
def cm_to_in(cm: float) -> float:
    """Convert centimeters to inches.

    Args:
        cm (float): Length in centimeters.

    Returns:
        float: Length in inches.
    """
    return cm / 2.54

device_pixel_ratio() -> float staticmethod

Get the device pixel ratio for high-DPI displays.

Returns:

Name Type Description
float float

Device pixel ratio (typically 1.0 for standard displays, >1.0 for high-DPI).

Source code in src/gsp/utils/unit_utils.py
40
41
42
43
44
45
46
47
48
49
50
51
52
@staticmethod
def device_pixel_ratio() -> float:
    """Get the device pixel ratio for high-DPI displays.

    Returns:
        float: Device pixel ratio (typically 1.0 for standard displays, >1.0 for high-DPI).
    """
    # detect if running on a macOS retina display or other high-DPI display
    # This is a placeholder implementation; actual detection may vary based on the GUI framework used.
    is_macosx = "darwin" in sys.platform
    if is_macosx:
        return 2.0  # Common value for retina displays
    return 1.0

in_to_cm(inches: float) -> float staticmethod

Convert inches to centimeters.

Parameters:

Name Type Description Default
inches float

Length in inches.

required

Returns:

Name Type Description
float float

Length in centimeters.

Source code in src/gsp/utils/unit_utils.py
16
17
18
19
20
21
22
23
24
25
26
@staticmethod
def in_to_cm(inches: float) -> float:
    """Convert inches to centimeters.

    Args:
        inches (float): Length in inches.

    Returns:
        float: Length in centimeters.
    """
    return inches * 2.54

pixel_to_point(pixel_size: float, dpi: float) -> float staticmethod

Convert pixel size to typographic point size based on DPI.

Parameters:

Name Type Description Default
pixel_size float

Size in pixels.

required
dpi float

Dots per inch of the display.

required

Returns:

Name Type Description
float float

Size in points.

Source code in src/gsp/utils/unit_utils.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@staticmethod
def pixel_to_point(pixel_size: float, dpi: float) -> float:
    """Convert pixel size to typographic point size based on DPI.

    Args:
        pixel_size (float): Size in pixels.
        dpi (float): Dots per inch of the display.

    Returns:
        float: Size in points.
    """
    inches_per_pixel = 1.0 / dpi
    points_per_inch = 72.0
    point_size = pixel_size * inches_per_pixel * points_per_inch
    return point_size

pixel_to_point_numpy(pixel_sizes: np.ndarray, dpi: float) -> np.ndarray staticmethod

Convert an array of pixel sizes to point sizes based on DPI.

Parameters:

Name Type Description Default
pixel_sizes numpy.ndarray

Array of sizes in pixels.

required
dpi float

Dots per inch of the display.

required

Returns:

Type Description
numpy.ndarray

np.ndarray: Array of sizes in points.

Source code in src/gsp/utils/unit_utils.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@staticmethod
def pixel_to_point_numpy(pixel_sizes: np.ndarray, dpi: float) -> np.ndarray:
    """Convert an array of pixel sizes to point sizes based on DPI.

    Args:
        pixel_sizes (np.ndarray): Array of sizes in pixels.
        dpi (float): Dots per inch of the display.

    Returns:
        np.ndarray: Array of sizes in points.
    """
    inches_per_pixel = 1.0 / dpi
    points_per_inch = 72.0
    point_sizes = pixel_sizes * inches_per_pixel * points_per_inch
    return point_sizes

point_to_pixel(point_size: float, dpi: float) -> float staticmethod

Convert point size to pixel size based on DPI.

Parameters:

Name Type Description Default
point_size float

Size in points.

required
dpi float

Dots per inch of the display.

required

Returns:

Name Type Description
float float

Size in pixels.

Source code in src/gsp/utils/unit_utils.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@staticmethod
def point_to_pixel(point_size: float, dpi: float) -> float:
    """Convert point size to pixel size based on DPI.

    Args:
        point_size (float): Size in points.
        dpi (float): Dots per inch of the display.

    Returns:
        float: Size in pixels.
    """
    inches_per_point = 1.0 / 72.0
    pixels_per_inch = dpi
    pixel_size = point_size * inches_per_point * pixels_per_inch
    return pixel_size

point_to_pixel_numpy(point_sizes: np.ndarray, dpi: float) -> np.ndarray staticmethod

Convert an array of point sizes to pixel sizes based on DPI.

Parameters:

Name Type Description Default
point_sizes numpy.ndarray

Array of sizes in points.

required
dpi float

Dots per inch of the display.

required

Returns:

Type Description
numpy.ndarray

np.ndarray: Array of sizes in pixels.

Source code in src/gsp/utils/unit_utils.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
@staticmethod
def point_to_pixel_numpy(point_sizes: np.ndarray, dpi: float) -> np.ndarray:
    """Convert an array of point sizes to pixel sizes based on DPI.

    Args:
        point_sizes (np.ndarray): Array of sizes in points.
        dpi (float): Dots per inch of the display.

    Returns:
        np.ndarray: Array of sizes in pixels.
    """
    inches_per_point = 1.0 / 72.0
    pixels_per_inch = dpi
    pixel_sizes = point_sizes * inches_per_point * pixels_per_inch
    return pixel_sizes

gsp.utils.uuid_utils

Utility functions for UUID generation.

UuidUtils

Utility class for generating UUIDs.

Source code in src/gsp/utils/uuid_utils.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class UuidUtils:
    """Utility class for generating UUIDs."""

    GSP_UUID_COUNTER: int = 0

    @staticmethod
    def generate_uuid() -> str:
        """Generate a UUID version 4.

        Returns:
            str: The generated UUID.
        """
        # if GSP_UUID_COUNTER is set, use a deterministic uuid for testing purposes
        # - uuid becomes "uuid-counter-<counter>"
        if "GSP_UUID_COUNTER" in os.environ:
            _uuid = UuidUtils.GSP_UUID_COUNTER
            UuidUtils.GSP_UUID_COUNTER += 1
            return f"uuid-counter-{_uuid}"

        # uuid4 = UuidUtils._generate_uuid_v4_with_uuid()
        uuid4 = UuidUtils._generate_uuid_v4_with_numpy()
        return uuid4

    @staticmethod
    def _generate_uuid_v4_with_numpy() -> str:
        """Generate a UUID version 4 using numpy for random byte generation.

        Thus it can be made deterministic by setting the numpy random seed.
        """
        # 1. Generate 16 random bytes (128 bits) using numpy
        # We use uint8 for byte representation
        random_bytes = np.random.randint(0, 256, size=16, dtype=np.uint8)

        # Convert the numpy array of bytes into a standard Python byte string
        # This is necessary because bit manipulation on numpy arrays is tricky/non-standard
        byte_string = bytes(random_bytes.tolist())

        # 2. Apply the UUID version 4 (variant 1) rules:

        # Rule 1: Set the four most significant bits of the 7th byte (octet 6) to 0100_2
        # This sets the UUID version to 4.
        # The 7th byte is at index 6 (0-indexed).
        # To set the first four bits to 0100 (4 in hex), we clear the upper 4 bits
        # and then set them to 4. (byte_string[6] & 0b1111) clears the upper bits,
        # then | 0x40 is wrong. It should be (byte_string[6] & 0x0F) | 0x40.

        # We must use a mutable structure to modify the bytes.
        # We'll use a standard Python list of integers (0-255) for easy modification.
        byte_list = list(byte_string)

        # Set Version (byte 6, index 6): 0x40 (0100xxxx)
        byte_list[6] = (byte_list[6] & 0x0F) | 0x40

        # Rule 2: Set the two most significant bits of the 9th byte (octet 8) to 10_2
        # This sets the UUID variant to 'Reserved (RFC 4122)'.
        # The 9th byte is at index 8.
        # To set the first two bits to 10 (8 in hex or 0x80), we clear the upper 2 bits
        # and then set them to 10. (byte_list[8] & 0x3F) clears the upper 2 bits,
        # then | 0x80 sets them to 10.
        byte_list[8] = (byte_list[8] & 0x3F) | 0x80

        # 3. Format the bytes into a standard UUID string format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
        # We convert the list back to bytes and then to hexadecimal.
        final_bytes = bytes(byte_list)
        hex_str = final_bytes.hex()

        # Insert hyphens
        uuid_v4 = f"{hex_str[0:8]}-{hex_str[8:12]}-{hex_str[12:16]}-{hex_str[16:20]}-{hex_str[20:32]}"

        return uuid_v4

    @staticmethod
    def _generate_uuid_v4_with_uuid() -> str:
        """Generate a UUID version 4 using the standard library uuid module."""
        import uuid

        uuid_v4 = str(uuid.uuid4())

        return uuid_v4

generate_uuid() -> str staticmethod

Generate a UUID version 4.

Returns:

Name Type Description
str str

The generated UUID.

Source code in src/gsp/utils/uuid_utils.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@staticmethod
def generate_uuid() -> str:
    """Generate a UUID version 4.

    Returns:
        str: The generated UUID.
    """
    # if GSP_UUID_COUNTER is set, use a deterministic uuid for testing purposes
    # - uuid becomes "uuid-counter-<counter>"
    if "GSP_UUID_COUNTER" in os.environ:
        _uuid = UuidUtils.GSP_UUID_COUNTER
        UuidUtils.GSP_UUID_COUNTER += 1
        return f"uuid-counter-{_uuid}"

    # uuid4 = UuidUtils._generate_uuid_v4_with_uuid()
    uuid4 = UuidUtils._generate_uuid_v4_with_numpy()
    return uuid4

Constants

gsp.constants

Common constants for GSP including color definitions.

Constants

Common constants in GSP. e.g. colors.

Source code in src/gsp/constants.py
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Constants:
    """Common constants in GSP. e.g. colors."""

    class Color:
        """Common colors as RGBA bytearrays.

        Each color is represented as a bytearray of four integers
        corresponding to the red, green, blue, and alpha (opacity) channels, respectively.
        Each channel value ranges from 0 to 255.
        """

        white = bytearray([255, 255, 255, 255])
        black = bytearray([0, 0, 0, 255])
        red = bytearray([255, 0, 0, 255])
        green = bytearray([0, 255, 0, 255])
        blue = bytearray([0, 0, 255, 255])

        yellow = bytearray([255, 255, 0, 255])
        magenta = bytearray([255, 0, 255, 255])
        cyan = bytearray([0, 255, 255, 255])

        light_gray = bytearray([211, 211, 211, 255])
        gray = bytearray([128, 128, 128, 255])
        dark_gray = bytearray([64, 64, 64, 255])

        transparent = bytearray([0, 0, 0, 0])

Color

Common colors as RGBA bytearrays.

Each color is represented as a bytearray of four integers corresponding to the red, green, blue, and alpha (opacity) channels, respectively. Each channel value ranges from 0 to 255.

Source code in src/gsp/constants.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Color:
    """Common colors as RGBA bytearrays.

    Each color is represented as a bytearray of four integers
    corresponding to the red, green, blue, and alpha (opacity) channels, respectively.
    Each channel value ranges from 0 to 255.
    """

    white = bytearray([255, 255, 255, 255])
    black = bytearray([0, 0, 0, 255])
    red = bytearray([255, 0, 0, 255])
    green = bytearray([0, 255, 0, 255])
    blue = bytearray([0, 0, 255, 255])

    yellow = bytearray([255, 255, 0, 255])
    magenta = bytearray([255, 0, 255, 255])
    cyan = bytearray([0, 255, 255, 255])

    light_gray = bytearray([211, 211, 211, 255])
    gray = bytearray([128, 128, 128, 255])
    dark_gray = bytearray([64, 64, 64, 255])

    transparent = bytearray([0, 0, 0, 0])