uim.model.inkdata.strokes API documentation

# Module `uim.model.inkdata.strokes`

Expand source code

    # -*- coding: utf-8 -*-
    # Copyright © 2021 Wacom Authors. All Rights Reserved.
    #
    #  Licensed under the Apache License, Version 2.0 (the "License");
    #  you may not use this file except in compliance with the License.
    #  You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    #  Unless required by applicable law or agreed to in writing, software
    #  distributed under the License is distributed on an "AS IS" BASIS,
    #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    #  See the License for the specific language governing permissions and
    #  limitations under the License.
    import ctypes
    import uuid
    from abc import ABC
    from enum import Enum
    from typing import Tuple, List, Optional

    import numpy as np

    from uim.codec.context.scheme import PrecisionScheme
    from uim.model.base import UUIDIdentifier, HashIdentifier
    from uim.model.inkdata.brush import BlendModeURIs
    from uim.model.semantics.structures import BoundingBox

    class LayoutMask(Enum):
        """
        Specifies the various geometric and appearance attributes of a path point as bit flags.
        """
        X = 0x1
        Y = 0x2
        Z = 0x4
        SIZE = 0x8
        ROTATION = 0x10
        RED = 0x20
        GREEN = 0x40
        BLUE = 0x80
        ALPHA = 0x100
        SCALE_X = 0x200
        SCALE_Y = 0x400
        SCALE_Z = 0x800
        OFFSET_X = 0x1000
        OFFSET_Y = 0x2000
        OFFSET_Z = 0x4000
        TANGENT_X = 0x8000
        TANGENT_Y = 0x10000

    class PathPointProperties(HashIdentifier):
        """
        PathPointProperties
        ===================
        A simple data model, which may hold size, color components and matrix transformational components.

        Parameters
        ----------
        size: float
            Size of the brush.
        red: float
            Color value [0, 1] for red channel.
        green: float
            Color value [0, 1] for green channel.
        blue: float
            Color value [0, 1] for blue channel.
        alpha: float
            Color value [0, 1] for alpha channel.
        rotation: float
            Brush rotation z value.
        scale_x: float
            Brush scale x value.
        scale_y: float
            Brush scale y value.
        scale_z: float
            Brush scale z [for 3D rendering].
        offset_x: float
            Brush offset x value.
        offset_y: float
            Brush offset y value.
        offset_z: float
            Brush offset z [for 3D rendering].

        References
        ----------
        [1] WILL SDK for ink - Rendering pipeline URL: https://developer-docs.wacom.com/sdk-for-ink/docs/pipeline
        [2] Ink Designer to configure rendering pipeline: http://ink-designer.trafficmanager.net/
        """

        def __init__(self, size: float = 0., red: float = 0., green: float = 0., blue: float = 0., alpha: float = 0.,
                     rotation: float = 0.,
                     scale_x: float = 0., scale_y: float = 0., scale_z: float = 0.,
                     offset_x: float = 0., offset_y: float = 0., offset_z: float = 0.):
            super().__init__()
            self.__size = size
            self.__red = red
            self.__green = green
            self.__blue = blue
            self.__alpha = alpha
            self.__rotation = rotation
            self.__scale_x = scale_x
            self.__scale_y = scale_y
            self.__scale_z = scale_z
            self.__offset_x = offset_x
            self.__offset_y = offset_y
            self.__offset_z = offset_z

        @staticmethod
        def color(rgba: int) -> Tuple[float, float, float, float]:
            """
            Decode integer encoded RBGA value into float.

            Parameters
            ----------
            rgba: int
                Color encoded in a single integer value

            Returns
            -------
            red - `float`
                Red value
            green - `float`
                Green value
            blue - `float`
                Blue value
            alpha - `float`
                Alpha value
            """
            red: float = ((rgba >> 24) & 0xFF) / 255.0
            green: float = ((rgba >> 16) & 0xFF) / 255.0
            blue: float = ((rgba >> 8) & 0xFF) / 255.0
            alpha: float = (rgba & 0xFF) / 255.0
            return red, green, blue, alpha

        @staticmethod
        def rgba(red: float, green: float, blue: float, alpha: float) -> int:
            """
            Encode RGBA values to a single integer value.

            Parameters
            ----------
            red: int -
                Red value
            green: int -
                Green value
            blue: int -
                Blue value
            alpha: int -
                Alpha value

            Returns
            -------
            rgba: int
                Color values encode as one single integer value
            """
            r: int = int(red * 255)
            g: int = int(green * 255)
            b: int = int(blue * 255)
            a: int = int(alpha * 255)
            return ctypes.c_int32((r << 24) | (g << 16) | (b << 8) | a).value

        @property
        def size(self) -> float:
            """Size of the brush; value between [0. - 1.]. (`float`)"""
            return self.__size

        @property
        def red(self) -> float:
            """Color value for red channel; value between [0. - 1.]. (`float`)"""
            return self.__red

        @property
        def green(self) -> float:
            """Color value for green channel; value between [0. - 1.]. (`float`)"""
            return self.__green

        @property
        def blue(self) -> float:
            """Color value for blue channel; value between [0. - 1.]. (`float`)"""
            return self.__blue

        @property
        def alpha(self) -> float:
            """Alpha value for channel; value between [0. - 1.]. (`float`)"""
            return self.__alpha

        @property
        def rotation(self) -> float:
            """Brush rotation. (`float`)"""
            return self.__rotation

        @property
        def scale_x(self) -> float:
            """Brush scale x value. (`float`)"""
            return self.__scale_x

        @property
        def scale_y(self) -> float:
            """Brush scale y value. (`float`)"""
            return self.__scale_y

        @property
        def scale_z(self) -> float:
            """Brush scale z value. (`float`)"""
            return self.__scale_z

        @property
        def offset_x(self) -> float:
            """Offset x value. (`float`)"""
            return self.__offset_x

        @property
        def offset_y(self) -> float:
            """Offset y value. (`float`)"""
            return self.__offset_y

        @property
        def offset_z(self) -> float:
            """Offset z value. (`float`)"""
            return self.__offset_z

        @size.setter
        def size(self, size: float):
            self.__size = size

        @red.setter
        def red(self, red: float):
            self.__red = red

        @green.setter
        def green(self, green: float):
            self.__green = green

        @blue.setter
        def blue(self, blue: float):
            self.__blue = blue

        @alpha.setter
        def alpha(self, alpha: float):
            self.__alpha = alpha

        @rotation.setter
        def rotation(self, rotation: float):
            self.__rotation = rotation

        @scale_x.setter
        def scale_x(self, scale_x: float):
            self.__scale_x = scale_x

        @scale_y.setter
        def scale_y(self, scale_y: float):
            self.__scale_y = scale_y

        @scale_z.setter
        def scale_z(self, scale_z: float):
            self.__scale_z = scale_z

        @offset_x.setter
        def offset_x(self, offset_x: float):
            self.__offset_x = offset_x

        @offset_y.setter
        def offset_y(self, offset_y: float):
            self.__offset_y = offset_y

        @offset_z.setter
        def offset_z(self, offset_z: float):
            self.__offset_z = offset_z

        def __tokenize__(self) -> list:
            return [self.size, self.red, self.green, self.blue, self.alpha, self.rotation, self.scale_x, self.scale_y,
                    self.scale_z, self.offset_x, self.offset_y, self.offset_z]

        def __repr__(self):
            return '<PathPointProperties: [size:={}, red:={}, green:={}, blue:={}, alpha:={}, rotation:={},' \
                   'scale x:={}, scale y:={}, scale z:={}, offset x:={}, offset y:={}, offset z:={}]>' \
                .format(self.size,
                        self.red, self.green, self.blue, self.alpha,
                        self.rotation,
                        self.scale_x, self.scale_y, self.scale_z,
                        self.offset_x, self.offset_y, self.offset_z)

    class Style(ABC):
        """
        Style
        =====
        The `Style` is defined as a combination of a `PathPointProperties` configuration, reference to a Brush,
        a random number generator seed value and rendering method type. Setting the Style property allows overriding of
        specific path point properties, color components and/or matrix transformational components.
        A `Style` with `PathPointProperties` configuration should be normally used to define constant path components.

        Parameters
        ----------
        properties: `PathPointProperties`
            Static values of properties which do not exist per per path point
        brush_uri: str
            Reference to Brush used for stroke rasterization
        particles_random_seed: int
            Particles random seed, required for particle strokes
        render_mode_uri: str
            Render mode URI
        """

        def __init__(self, properties: PathPointProperties = None, brush_uri: str = None, particles_random_seed: int = 0,
                     render_mode_uri: str = BlendModeURIs.SOURCE_OVER):
            self.__properties = properties if properties is not None else PathPointProperties()
            self.__brush_uri = brush_uri
            self.__particles_random_seed = particles_random_seed
            self.__render_mode_URI = render_mode_uri if render_mode_uri is not None and render_mode_uri != '' \
                else BlendModeURIs.SOURCE_OVER

        @property
        def path_point_properties(self) -> PathPointProperties:
            """Static values of properties which do not exist per per path point. (`PathPointProperties`, read-only)"""
            return self.__properties

        @property
        def brush_uri(self) -> str:
            """Reference to Brush used for stroke rasterization using the URI. (`str`, read-only)"""
            return self.__brush_uri

        @property
        def particles_random_seed(self):
            """Particles random seed, required for particle strokes.  (`int`, read-only) """
            return self.__particles_random_seed

        @property
        def render_mode_uri(self):
            """Defines additional information about stroke visualisation, such as ERASER. (`str`)"""
            return self.__render_mode_URI

        @render_mode_uri.setter
        def render_mode_uri(self, uri: str):
            self.__render_mode_URI = uri

        def __repr__(self):
            return '<Style : [id:={}, particles_random_seed:={}, render mode:={}>' \
                .format(self.__brush_uri,
                        self.__particles_random_seed,
                        self.__render_mode_URI)

    class Spline(ABC):
        """
        Spline
        ======
        The Catmull-Rom spline is defined in the scope of the Stroke using the following properties:

        - **ts, tf** - Spline start and end parameters
        - **spline** - a sequence of spline data points
        - **color** - a sequence of color values (per spline data point; if provided, the length of this sequence must be
                      equal to the spline points count)

        Parameters
        ----------
        layout_mask: int
            Configured layout mask
        data: List[float]
            List of spline values
        ts: float
            Start parameter
        tf: float
            Final parameter
        """

        def __init__(self, layout_mask: int, data: List[float], ts: float = 0., tf: float = 1.):
            self.__layout_mask: int = layout_mask
            self.__data: List[float] = data
            self.__ts: float = ts
            self.__tf: float = tf

        @property
        def data(self) -> List[float]:
            """Gets or sets a list of spline values. (`List[float]`, read-only)"""
            return self.__data

        @property
        def layout_mask(self) -> int:
            """Gets a bitmask representation of the spline's data layout. (`int`, read-only)"""
            return self.__layout_mask

        @property
        def ts(self) -> float:
            """Start parameter for the first Catmull-Rom segment. (`float`, read-only)"""
            return self.__ts

        @property
        def tf(self) -> float:
            """Final parameter for the last Catmull-Rom segment. (`float`, read-only)"""
            return self.__tf

        def __repr__(self):
            return f'<Spline : [mask:={self.layout_mask}]>'

    class Stroke(UUIDIdentifier):
        """
        Stroke Geometry
        ---------------
        The geometry of an ink stroke is represented by its Stroke.
        A Stroke is defined as a combination of:

            - A Catmull-Rom spline in the form of a sequence of data points (mandatory), including per-point
              transformational data (optional)
            - Rendering configuration about how the spline should be visualized (optional)
            - Reference to raw input data (SensorData instance), which the path originates from (optional)

        Parameters
        ----------
         sid: `UUID`
            Stroke unique identifier
        sensor_data_offset: `int`
            Index of points mapping between raw and processed paths.
        sensor_data_id: `UUID`
            Reference UUID of sensor data.
        sensor_data_mapping: list
            Explicit mapping between indices of Path and SensorData, used when input rate is very high and
            provides unwanted points.
        style: `Style`
            The Style object associated with this stroke.
        random_seed: `int`
            A random seed used for randomly generated attributes of a stroke.
        spline: `Spline`
            The stroke's Catmull-Rom spline.

        Examples
        --------
        >>> from uim.model.inkdata.brush import BrushPolygon, BrushPolygonUri, RasterBrush, RotationMode, BlendMode
        >>> from uim.model.inkdata.strokes import Spline, Style, Stroke, LayoutMask
        >>>
        >>> raster_brush_0: RasterBrush = RasterBrush(
        >>> name="app://qa-test-app/raster-brush/MyRasterBrush",
        >>> spacing=10., scattering=5., rotation=RotationMode.TRAJECTORY, shape_textures=[bytes([10, 20]),
        >>>                                                                               bytes([30, 20])],
        >>> fill_width=2.0, fill_height=0.3,
        >>> fill_texture=bytes([10, 10, 20, 15, 17, 20, 25, 16, 34, 255, 23, 0, 34, 255, 23, 255]),
        >>> randomize_fill=False, blend_mode=BlendMode.SOURCE_OVER)
        >>> # Create a spline object - 9 data points, each consisting of X, Y, Size, Red, Green, Blue, Alpha
        >>> spline_1: Spline = Spline(
        >>>     LayoutMask.X.value | LayoutMask.Y.value | LayoutMask.SIZE.value | LayoutMask.RED.value | LayoutMask.GREEN.value
        >>>     | LayoutMask.BLUE.value | LayoutMask.ALPHA.value,
        >>>     [10.0, 10.7, 1.0, 0.5, 0.0, 0.1, 1.0,
        >>>      21.0, 20.2, 2.0, 0.9, 0.4, 0.2, 0.8,
        >>>      30.0, 12.4, 2.1, 0.7, 0.1, 0.1, 0.7,
        >>>      40.0, 15.0, 1.5, 0.3, 0.5, 0.4, 1.0,
        >>>      50.0, 45.0, 1.0, 0.3, 0.5, 0.4, 1.0,
        >>>      41.0, 53.0, 1.1, 0.2, 0.3, 0.5, 0.9,
        >>>      33.0, 73.0, 1.2, 0.6, 0.7, 0.4, 0.8,
        >>>      20.0, 84.0, 1.3, 0.7, 0.8, 0.3, 0.7,
        >>>      10.0, 91.0, 1.1, 0.7, 0.9, 0.2, 0.6]
        >>> )
        >>>
        >>> # Create a style
        >>> style_1: Style = Style(brush_uri=raster_brush_0.name)
        >>> style_1.path_point_properties.rotation = 0.35
        >>>
        >>> # The render mode URI can also be app specific like app://blabla
        >>> # The URI will://rasterization/3.0/blend-mode/SourceOver is assumed and must not be set.
        >>> style_1.render_mode_uri = "will://rasterization/3.0/blend-mode/DestinationOver"
        >>>
        >>> # Create a stroke object. Note that it just exists, but is not in the model yet.
        >>> stroke_1: Stroke = Stroke(UUIDIdentifier.id_generator(), spline=spline_1, style=style_1)
        """

        def __init__(self, sid: uuid.UUID = None, sensor_data_offset: int = None, sensor_data_id: uuid.UUID = None,
                     sensor_data_mapping: list = None, style: Style = None, random_seed: int = 0, property_index: int = 0,
                     spline: Spline = None):
            super().__init__(sid)
            self.__start_parameter: float = 0.
            self.__end_parameter: float = 0.
            self.__spline_x: List[float] = []
            self.__spline_y: List[float] = []
            self.__spline_z: List[float] = []
            self.__size: List[float] = []
            self.__rotation: List[float] = []
            self.__scale_x: List[float] = []
            self.__scale_y: List[float] = []
            self.__scale_z: List[float] = []
            self.__offset_x: List[float] = []
            self.__offset_y: List[float] = []
            self.__offset_z: List[float] = []
            self.__red: List[int] = []
            self.__green: List[int] = []
            self.__blue: List[int] = []
            self.__alpha: List[int] = []
            self.__tangent_x: List[float] = []
            self.__tangent_y: List[float] = []
            self.__sensor_data_id: uuid.UUID = sensor_data_id
            self.__sensor_data_offset: int = sensor_data_offset or 0
            self._sensor_data_mapping: list = sensor_data_mapping or []
            self.__style: Style = style
            self.__random_seed: int = random_seed
            self.__properties_index: int = property_index
            self.__timestamp_cache = None
            self.__pressure_cache = None
            self.__precision_scheme: Optional[PrecisionScheme] = None
            if spline is not None:
                self.__import__(spline)

        def set_timestamp_values(self, timestamp_values):
            self.__timestamp_cache = timestamp_values

        def get_timestamp_values(self):
            return self.__timestamp_cache

        def set_pressure_values(self, pressure_values):
            self.__pressure_cache = pressure_values

        def get_pressure_values(self):
            return self.__pressure_cache

        @property
        def layout_mask(self) -> int:
            """Layout mask for the stroke. (`int`)"""
            mask: int = 0
            if len(self.__spline_x) > 0:
                mask |= LayoutMask.X.value
            if len(self.__spline_y) > 0:
                mask |= LayoutMask.Y.value
            if len(self.__spline_z) > 0:
                mask |= LayoutMask.Z.value
            if len(self.__size) > 0:
                mask |= LayoutMask.SIZE.value
            if len(self.__rotation) > 0:
                mask |= LayoutMask.ROTATION.value
            if len(self.__red) > 0:
                mask |= LayoutMask.RED.value
            if len(self.__green) > 0:
                mask |= LayoutMask.GREEN.value
            if len(self.__blue) > 0:
                mask |= LayoutMask.BLUE.value
            if len(self.__alpha) > 0:
                mask |= LayoutMask.ALPHA.value
            if len(self.__scale_x) > 0:
                mask |= LayoutMask.SCALE_X.value
            if len(self.__scale_y) > 0:
                mask |= LayoutMask.SCALE_Y.value
            if len(self.__scale_z) > 0:
                mask |= LayoutMask.SCALE_Z.value
            if len(self.__offset_x) > 0:
                mask |= LayoutMask.OFFSET_X.value
            if len(self.__offset_y) > 0:
                mask |= LayoutMask.OFFSET_Y.value
            if len(self.__offset_z) > 0:
                mask |= LayoutMask.OFFSET_Z.value
            if len(self.__tangent_x) > 0:
                mask |= LayoutMask.TANGENT_X.value
            if len(self.__tangent_y) > 0:
                mask |= LayoutMask.TANGENT_Y.value
            return mask

        @property
        def properties_index(self) -> int:
            """Setting the properties index. (`int`)"""
            return self.__properties_index

        @properties_index.setter
        def properties_index(self, properties_index: int):
            self.__properties_index = properties_index

        @property
        def start_parameter(self) -> float:
            """Start parameter of the stroke. (`float`)"""
            return self.__start_parameter

        @start_parameter.setter
        def start_parameter(self, s: float):
            self.__start_parameter = s

        @property
        def end_parameter(self) -> float:
            """End parameter of the stroke. (`float`)"""
            return self.__end_parameter

        @end_parameter.setter
        def end_parameter(self, e: float):
            self.__end_parameter = e

        @property
        def precision_scheme(self) -> PrecisionScheme:
            """An object that defines the precisions used for storing the stroke data. If set to None, stroke data is stored
            using floating point values. (`PrecisionScheme`)"""
            return self.__precision_scheme

        @precision_scheme.setter
        def precision_scheme(self, precision_scheme: PrecisionScheme):
            self.__precision_scheme = precision_scheme

        @property
        def sizes(self) -> List[float]:
            """List of size values. (`List[float]`)"""
            return self.__size

        @sizes.setter
        def sizes(self, size: list):
            self.__size = size

        @property
        def red(self) -> List[int]:
            """List of color values [0, 255] for red channel. (`List[int]`)"""
            return self.__red

        @red.setter
        def red(self, values: List[int]):
            self.__red = values

        @property
        def green(self) -> List[int]:
            """Color values [0, 255] for green channel. (`List[int]`)"""
            return self.__green

        @green.setter
        def green(self, values: List[int]):
            self.__green = values

        @property
        def blue(self) -> List[int]:
            """Color values [0, 255] for blue channel. (`List[int]`)"""
            return self.__blue

        @blue.setter
        def blue(self, values: List[int]):
            self.__blue = values

        @property
        def alpha(self) -> List[int]:
            """Color values [0, 255] for alpha channel. (`List[int]`)"""
            return self.__alpha

        @alpha.setter
        def alpha(self, values: List[int]):
            self.__alpha = values

        @property
        def rotations(self) -> List[float]:
            """List of rotations. (`List[float]`)"""
            return self.__rotation

        @rotations.setter
        def rotations(self, values: List[float]):
            self.__rotation = values

        @property
        def splines_x(self) -> List[float]:
            """List of splines x. (`List[float]`)"""
            return self.__spline_x

        @splines_x.setter
        def splines_x(self, spline_x: List[float]):
            self.__spline_x = spline_x

        @property
        def splines_y(self) -> List[float]:
            """List of splines y. (`List[float]`)"""
            return self.__spline_y

        @splines_y.setter
        def splines_y(self, spline_y: List[float]):
            self.__spline_y = spline_y

        @property
        def splines_z(self) -> List[float]:
            """List of splines z. (`List[float]`)"""
            return self.__spline_z

        @splines_z.setter
        def splines_z(self, spline_z: List[float]):
            self.__spline_z = spline_z

        @property
        def scales_x(self) -> List[float]:
            """List of x scales. (`List[float]`)"""
            return self.__scale_x

        @scales_x.setter
        def scales_x(self, scale: List[float]):
            self.__scale_x = scale

        @property
        def scales_y(self) -> List[float]:
            """List of y scales. (`List[float]`)"""
            return self.__scale_y

        @scales_y.setter
        def scales_y(self, scale: List[float]):
            self.__scale_y = scale

        @property
        def scales_z(self) -> List[float]:
            """List of z scales. (`List[float]`)"""
            return self.__scale_z

        @scales_z.setter
        def scales_z(self, scale: list):
            self.__scale_z = scale

        @property
        def offsets_x(self) -> List[float]:
            """List of x offsets."""
            return self.__offset_x

        @offsets_x.setter
        def offsets_x(self, offset: List[float]):
            self.__offset_x = offset

        @property
        def offsets_y(self) -> List[float]:
            """List of y offsets. (`List[float]`)"""
            return self.__offset_y

        @offsets_y.setter
        def offsets_y(self, offset: List[float]):
            self.__offset_y = offset

        @property
        def offsets_z(self) -> List[float]:
            """List of z offsets. (`List[float]`)"""
            return self.__offset_z

        @offsets_z.setter
        def offsets_z(self, offset: List[float]):
            self.__offset_z = offset

        @property
        def sensor_data_offset(self) -> int:
            """Index of points mapping between raw and processed paths. (`int`)"""
            return self.__sensor_data_offset

        @property
        def sensor_data_id(self) -> uuid.UUID:
            """Reference id sensor data. (`UUID`)"""
            return self.__sensor_data_id

        @property
        def sensor_data_mapping(self) -> List[int]:
            """Explicit mapping between indices of Path and SensorData, used when input rate is very high and
            provides unwanted points. (`List[int]`)"""
            return self._sensor_data_mapping

        @property
        def style(self) -> Style:
            """Style that is applied to the path of the stroke. (`Style`)"""
            return self.__style

        @style.setter
        def style(self, style: Style):
            self.__style = style

        @property
        def spline_min_x(self) -> float:
            """Minimum value of x spline. (`float`)"""
            return np.min(self.__spline_x)

        @property
        def spline_min_y(self) -> float:
            """Minimum value of y spline. (`float`)"""
            return np.min(self.__spline_y)

        @property
        def spline_max_x(self) -> float:
            """Maximum value of x spline. (`float`)"""
            return np.max(self.__spline_x)

        @property
        def spline_max_y(self) -> float:
            """Maximum value of y spline. (`float`)"""
            return np.max(self.__spline_y)

        @property
        def points_count(self) -> int:
            """Number of points of sample points. (`int`)"""
            return len(self.__spline_x)

        @property
        def bounding_box(self) -> BoundingBox:
            """Bounding box for path stroke. (`BoundingBox`)"""
            x_min = self.spline_min_x
            x_max = self.spline_max_x
            y_min = self.spline_min_y
            y_max = self.spline_max_y
            return BoundingBox(x=x_min, y=y_min, width=x_max - x_min, height=y_max - y_min)

        def __import__(self, spline: Spline):
            # The content from spline is imported with the appropriate Layout mask being set.
            self.__start_parameter = spline.ts
            self.__end_parameter = spline.tf
            idx: int = 0
            while idx < len(spline.data):
                if spline.layout_mask & LayoutMask.X.value:
                    self.__spline_x.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.Y.value:
                    self.__spline_y.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.Z.value:
                    self.__spline_z.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.SIZE.value:
                    self.__size.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.ROTATION.value:
                    self.__rotation.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.RED.value:
                    self.__red.append(int(spline.data[idx] * 255))
                    idx += 1
                if spline.layout_mask & LayoutMask.GREEN.value:
                    self.__green.append(int(spline.data[idx] * 255))
                    idx += 1
                if spline.layout_mask & LayoutMask.BLUE.value:
                    self.__blue.append(int(spline.data[idx] * 255))
                    idx += 1
                if spline.layout_mask & LayoutMask.ALPHA.value:
                    self.__alpha.append(int(spline.data[idx] * 255))
                    idx += 1
                if spline.layout_mask & LayoutMask.SCALE_X.value:
                    self.__scale_x.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.SCALE_Y.value:
                    self.__scale_y.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.SCALE_Z.value:
                    self.__scale_z.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.OFFSET_X.value:
                    self.__offset_x.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.OFFSET_Y.value:
                    self.__offset_y.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.OFFSET_Z.value:
                    self.__offset_z.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.TANGENT_X.value:
                    self.__tangent_x.append(spline.data[idx])
                    idx += 1
                if spline.layout_mask & LayoutMask.TANGENT_Y.value:
                    self.__tangent_y.append(spline.data[idx])
                    idx += 1

        def __repr__(self):
            return f'<Stroke : [id:={self.id_h_form}], [num points:={self.points_count}, layout mask:={self.layout_mask}]>'

## Classes

`class LayoutMask (value, names=None, *, module=None, qualname=None, type=None, start=1)`

  Specifies the various geometric and appearance attributes of a path point as bit flags.

  Expand source code

      class LayoutMask(Enum):
          """
          Specifies the various geometric and appearance attributes of a path point as bit flags.
          """
          X = 0x1
          Y = 0x2
          Z = 0x4
          SIZE = 0x8
          ROTATION = 0x10
          RED = 0x20
          GREEN = 0x40
          BLUE = 0x80
          ALPHA = 0x100
          SCALE_X = 0x200
          SCALE_Y = 0x400
          SCALE_Z = 0x800
          OFFSET_X = 0x1000
          OFFSET_Y = 0x2000
          OFFSET_Z = 0x4000
          TANGENT_X = 0x8000
          TANGENT_Y = 0x10000

  ### Ancestors

  - enum.Enum

  ### Class variables

  `var ALPHA`
  `var BLUE`
  `var GREEN`
  `var OFFSET_X`
  `var OFFSET_Y`
  `var OFFSET_Z`
  `var RED`
  `var ROTATION`
  `var SCALE_X`
  `var SCALE_Y`
  `var SCALE_Z`
  `var SIZE`
  `var TANGENT_X`
  `var TANGENT_Y`
  `var X`
  `var Y`
  `var Z`
`class PathPointProperties (size: float = 0.0, red: float = 0.0, green: float = 0.0, blue: float = 0.0, alpha: float = 0.0, rotation: float = 0.0, scale_x: float = 0.0, scale_y: float = 0.0, scale_z: float = 0.0, offset_x: float = 0.0, offset_y: float = 0.0, offset_z: float = 0.0)`

  # PathPointProperties

  A simple data model, which may hold size, color components and matrix transformational components.

  ## Parameters

  `size` :`float`
    Size of the brush.
  `red` :`float`
    Color value [0, 1] for red channel.
  `green` :`float`
    Color value [0, 1] for green channel.
  `blue` :`float`
    Color value [0, 1] for blue channel.
  `alpha` :`float`
    Color value [0, 1] for alpha channel.
  `rotation` :`float`
    Brush rotation z value.
  `scale_x` :`float`
    Brush scale x value.
  `scale_y` :`float`
    Brush scale y value.
  `scale_z` :`float`
    Brush scale z [for 3D rendering].
  `offset_x` :`float`
    Brush offset x value.
  `offset_y` :`float`
    Brush offset y value.
  `offset_z` :`float`
    Brush offset z [for 3D rendering].

  ## References

  [1] WILL SDK for ink - Rendering pipeline URL: https://developer-docs.wacom.com/sdk-for-ink/docs/pipeline [2] Ink Designer to configure rendering pipeline: http://ink-designer.trafficmanager.net/

  Expand source code

      class PathPointProperties(HashIdentifier):
          """
          PathPointProperties
          ===================
          A simple data model, which may hold size, color components and matrix transformational components.

          Parameters
          ----------
          size: float
              Size of the brush.
          red: float
              Color value [0, 1] for red channel.
          green: float
              Color value [0, 1] for green channel.
          blue: float
              Color value [0, 1] for blue channel.
          alpha: float
              Color value [0, 1] for alpha channel.
          rotation: float
              Brush rotation z value.
          scale_x: float
              Brush scale x value.
          scale_y: float
              Brush scale y value.
          scale_z: float
              Brush scale z [for 3D rendering].
          offset_x: float
              Brush offset x value.
          offset_y: float
              Brush offset y value.
          offset_z: float
              Brush offset z [for 3D rendering].

          References
          ----------
          [1] WILL SDK for ink - Rendering pipeline URL: https://developer-docs.wacom.com/sdk-for-ink/docs/pipeline
          [2] Ink Designer to configure rendering pipeline: http://ink-designer.trafficmanager.net/
          """

          def __init__(self, size: float = 0., red: float = 0., green: float = 0., blue: float = 0., alpha: float = 0.,
                       rotation: float = 0.,
                       scale_x: float = 0., scale_y: float = 0., scale_z: float = 0.,
                       offset_x: float = 0., offset_y: float = 0., offset_z: float = 0.):
              super().__init__()
              self.__size = size
              self.__red = red
              self.__green = green
              self.__blue = blue
              self.__alpha = alpha
              self.__rotation = rotation
              self.__scale_x = scale_x
              self.__scale_y = scale_y
              self.__scale_z = scale_z
              self.__offset_x = offset_x
              self.__offset_y = offset_y
              self.__offset_z = offset_z

          @staticmethod
          def color(rgba: int) -> Tuple[float, float, float, float]:
              """
              Decode integer encoded RBGA value into float.

              Parameters
              ----------
              rgba: int
                  Color encoded in a single integer value

              Returns
              -------
              red - `float`
                  Red value
              green - `float`
                  Green value
              blue - `float`
                  Blue value
              alpha - `float`
                  Alpha value
              """
              red: float = ((rgba >> 24) & 0xFF) / 255.0
              green: float = ((rgba >> 16) & 0xFF) / 255.0
              blue: float = ((rgba >> 8) & 0xFF) / 255.0
              alpha: float = (rgba & 0xFF) / 255.0
              return red, green, blue, alpha

          @staticmethod
          def rgba(red: float, green: float, blue: float, alpha: float) -> int:
              """
              Encode RGBA values to a single integer value.

              Parameters
              ----------
              red: int -
                  Red value
              green: int -
                  Green value
              blue: int -
                  Blue value
              alpha: int -
                  Alpha value

              Returns
              -------
              rgba: int
                  Color values encode as one single integer value
              """
              r: int = int(red * 255)
              g: int = int(green * 255)
              b: int = int(blue * 255)
              a: int = int(alpha * 255)
              return ctypes.c_int32((r << 24) | (g << 16) | (b << 8) | a).value

          @property
          def size(self) -> float:
              """Size of the brush; value between [0. - 1.]. (`float`)"""
              return self.__size

          @property
          def red(self) -> float:
              """Color value for red channel; value between [0. - 1.]. (`float`)"""
              return self.__red

          @property
          def green(self) -> float:
              """Color value for green channel; value between [0. - 1.]. (`float`)"""
              return self.__green

          @property
          def blue(self) -> float:
              """Color value for blue channel; value between [0. - 1.]. (`float`)"""
              return self.__blue

          @property
          def alpha(self) -> float:
              """Alpha value for channel; value between [0. - 1.]. (`float`)"""
              return self.__alpha

          @property
          def rotation(self) -> float:
              """Brush rotation. (`float`)"""
              return self.__rotation

          @property
          def scale_x(self) -> float:
              """Brush scale x value. (`float`)"""
              return self.__scale_x

          @property
          def scale_y(self) -> float:
              """Brush scale y value. (`float`)"""
              return self.__scale_y

          @property
          def scale_z(self) -> float:
              """Brush scale z value. (`float`)"""
              return self.__scale_z

          @property
          def offset_x(self) -> float:
              """Offset x value. (`float`)"""
              return self.__offset_x

          @property
          def offset_y(self) -> float:
              """Offset y value. (`float`)"""
              return self.__offset_y

          @property
          def offset_z(self) -> float:
              """Offset z value. (`float`)"""
              return self.__offset_z

          @size.setter
          def size(self, size: float):
              self.__size = size

          @red.setter
          def red(self, red: float):
              self.__red = red

          @green.setter
          def green(self, green: float):
              self.__green = green

          @blue.setter
          def blue(self, blue: float):
              self.__blue = blue

          @alpha.setter
          def alpha(self, alpha: float):
              self.__alpha = alpha

          @rotation.setter
          def rotation(self, rotation: float):
              self.__rotation = rotation

          @scale_x.setter
          def scale_x(self, scale_x: float):
              self.__scale_x = scale_x

          @scale_y.setter
          def scale_y(self, scale_y: float):
              self.__scale_y = scale_y

          @scale_z.setter
          def scale_z(self, scale_z: float):
              self.__scale_z = scale_z

          @offset_x.setter
          def offset_x(self, offset_x: float):
              self.__offset_x = offset_x

          @offset_y.setter
          def offset_y(self, offset_y: float):
              self.__offset_y = offset_y

          @offset_z.setter
          def offset_z(self, offset_z: float):
              self.__offset_z = offset_z

          def __tokenize__(self) -> list:
              return [self.size, self.red, self.green, self.blue, self.alpha, self.rotation, self.scale_x, self.scale_y,
                      self.scale_z, self.offset_x, self.offset_y, self.offset_z]

          def __repr__(self):
              return '<PathPointProperties: [size:={}, red:={}, green:={}, blue:={}, alpha:={}, rotation:={},' \
                     'scale x:={}, scale y:={}, scale z:={}, offset x:={}, offset y:={}, offset z:={}]>' \
                  .format(self.size,
                          self.red, self.green, self.blue, self.alpha,
                          self.rotation,
                          self.scale_x, self.scale_y, self.scale_z,
                          self.offset_x, self.offset_y, self.offset_z)

  ### Ancestors

  - HashIdentifier
  - Identifier
  - abc.ABC

  ### Class variables

  `var SEPARATOR : str`

  ### Static methods

  `def color(rgba: int) ‑> Tuple[float, float, float, float]`

    Decode integer encoded RBGA value into float.

    ## Parameters

    `rgba` :`int`
      Color encoded in a single integer value

    ## Returns

    red - `float` Red value green - `float` Green value blue - `float` Blue value alpha - `float` Alpha value

    Expand source code

        @staticmethod
        def color(rgba: int) -> Tuple[float, float, float, float]:
            """
            Decode integer encoded RBGA value into float.

            Parameters
            ----------
            rgba: int
                Color encoded in a single integer value

            Returns
            -------
            red - `float`
                Red value
            green - `float`
                Green value
            blue - `float`
                Blue value
            alpha - `float`
                Alpha value
            """
            red: float = ((rgba >> 24) & 0xFF) / 255.0
            green: float = ((rgba >> 16) & 0xFF) / 255.0
            blue: float = ((rgba >> 8) & 0xFF) / 255.0
            alpha: float = (rgba & 0xFF) / 255.0
            return red, green, blue, alpha

  `def rgba(red: float, green: float, blue: float, alpha: float) ‑> int`

    Encode RGBA values to a single integer value.

    ## Parameters

    `red` :`int -`
      Red value
    `green` :`int -`
      Green value
    `blue` :`int -`
      Blue value
    `alpha` :`int -`
      Alpha value

    ## Returns

    `rgba` :`int`
      Color values encode as one single integer value
    Expand source code

        @staticmethod
        def rgba(red: float, green: float, blue: float, alpha: float) -> int:
            """
            Encode RGBA values to a single integer value.

            Parameters
            ----------
            red: int -
                Red value
            green: int -
                Green value
            blue: int -
                Blue value
            alpha: int -
                Alpha value

            Returns
            -------
            rgba: int
                Color values encode as one single integer value
            """
            r: int = int(red * 255)
            g: int = int(green * 255)
            b: int = int(blue * 255)
            a: int = int(alpha * 255)
            return ctypes.c_int32((r << 24) | (g << 16) | (b << 8) | a).value

  ### Instance variables

  `var alpha : float`

    Alpha value for channel; value between [0. - 1.]. (`float`)

    Expand source code

        @property
        def alpha(self) -> float:
            """Alpha value for channel; value between [0. - 1.]. (`float`)"""
            return self.__alpha

  `var blue : float`

    Color value for blue channel; value between [0. - 1.]. (`float`)

    Expand source code

        @property
        def blue(self) -> float:
            """Color value for blue channel; value between [0. - 1.]. (`float`)"""
            return self.__blue

  `var green : float`

    Color value for green channel; value between [0. - 1.]. (`float`)

    Expand source code

        @property
        def green(self) -> float:
            """Color value for green channel; value between [0. - 1.]. (`float`)"""
            return self.__green

  `var offset_x : float`

    Offset x value. (`float`)

    Expand source code

        @property
        def offset_x(self) -> float:
            """Offset x value. (`float`)"""
            return self.__offset_x

  `var offset_y : float`

    Offset y value. (`float`)

    Expand source code

        @property
        def offset_y(self) -> float:
            """Offset y value. (`float`)"""
            return self.__offset_y

  `var offset_z : float`

    Offset z value. (`float`)

    Expand source code

        @property
        def offset_z(self) -> float:
            """Offset z value. (`float`)"""
            return self.__offset_z

  `var red : float`

    Color value for red channel; value between [0. - 1.]. (`float`)

    Expand source code

        @property
        def red(self) -> float:
            """Color value for red channel; value between [0. - 1.]. (`float`)"""
            return self.__red

  `var rotation : float`

    Brush rotation. (`float`)

    Expand source code

        @property
        def rotation(self) -> float:
            """Brush rotation. (`float`)"""
            return self.__rotation

  `var scale_x : float`

    Brush scale x value. (`float`)

    Expand source code

        @property
        def scale_x(self) -> float:
            """Brush scale x value. (`float`)"""
            return self.__scale_x

  `var scale_y : float`

    Brush scale y value. (`float`)

    Expand source code

        @property
        def scale_y(self) -> float:
            """Brush scale y value. (`float`)"""
            return self.__scale_y

  `var scale_z : float`

    Brush scale z value. (`float`)

    Expand source code

        @property
        def scale_z(self) -> float:
            """Brush scale z value. (`float`)"""
            return self.__scale_z

  `var size : float`

    Size of the brush; value between [0. - 1.]. (`float`)

    Expand source code

        @property
        def size(self) -> float:
            """Size of the brush; value between [0. - 1.]. (`float`)"""
            return self.__size

  ### Inherited members

  - `HashIdentifier`:
    - `from_bytes`
    - `id`
    - `id_h_form`
    - `id_s_form`
    - `str_to_uimid`
    - `uimid_to_h_form`
    - `uimid_to_s_form`
`class Spline (layout_mask: int, data: List[float], ts: float = 0.0, tf: float = 1.0)`

  # Spline

  The Catmull-Rom spline is defined in the scope of the Stroke using the following properties:

  - ts, tf - Spline start and end parameters
  - spline - a sequence of spline data points
  - color - a sequence of color values (per spline data point; if provided, the length of this sequence must be equal to the spline points count)

  ## Parameters

  `layout_mask` :`int`
    Configured layout mask
  `data` :`List[float]`
    List of spline values
  `ts` :`float`
    Start parameter
  `tf` :`float`
    Final parameter
  Expand source code

      class Spline(ABC):
          """
          Spline
          ======
          The Catmull-Rom spline is defined in the scope of the Stroke using the following properties:

          - **ts, tf** - Spline start and end parameters
          - **spline** - a sequence of spline data points
          - **color** - a sequence of color values (per spline data point; if provided, the length of this sequence must be
                        equal to the spline points count)

          Parameters
          ----------
          layout_mask: int
              Configured layout mask
          data: List[float]
              List of spline values
          ts: float
              Start parameter
          tf: float
              Final parameter
          """

          def __init__(self, layout_mask: int, data: List[float], ts: float = 0., tf: float = 1.):
              self.__layout_mask: int = layout_mask
              self.__data: List[float] = data
              self.__ts: float = ts
              self.__tf: float = tf

          @property
          def data(self) -> List[float]:
              """Gets or sets a list of spline values. (`List[float]`, read-only)"""
              return self.__data

          @property
          def layout_mask(self) -> int:
              """Gets a bitmask representation of the spline's data layout. (`int`, read-only)"""
              return self.__layout_mask

          @property
          def ts(self) -> float:
              """Start parameter for the first Catmull-Rom segment. (`float`, read-only)"""
              return self.__ts

          @property
          def tf(self) -> float:
              """Final parameter for the last Catmull-Rom segment. (`float`, read-only)"""
              return self.__tf

          def __repr__(self):
              return f'<Spline : [mask:={self.layout_mask}]>'

  ### Ancestors

  - abc.ABC

  ### Instance variables

  `var data : List[float]`

    Gets or sets a list of spline values. (`List[float]`, read-only)

    Expand source code

        @property
        def data(self) -> List[float]:
            """Gets or sets a list of spline values. (`List[float]`, read-only)"""
            return self.__data

  `var layout_mask : int`

    Gets a bitmask representation of the spline's data layout. (`int`, read-only)

    Expand source code

        @property
        def layout_mask(self) -> int:
            """Gets a bitmask representation of the spline's data layout. (`int`, read-only)"""
            return self.__layout_mask

  `var tf : float`

    Final parameter for the last Catmull-Rom segment. (`float`, read-only)

    Expand source code

        @property
        def tf(self) -> float:
            """Final parameter for the last Catmull-Rom segment. (`float`, read-only)"""
            return self.__tf

  `var ts : float`

    Start parameter for the first Catmull-Rom segment. (`float`, read-only)

    Expand source code

        @property
        def ts(self) -> float:
            """Start parameter for the first Catmull-Rom segment. (`float`, read-only)"""
            return self.__ts

`class Stroke (sid: uuid.UUID = None, sensor_data_offset: int = None, sensor_data_id: uuid.UUID = None, sensor_data_mapping: list = None, style: Style = None, random_seed: int = 0, property_index: int = 0, spline: Spline = None)`

  ## Stroke Geometry

  The geometry of an ink stroke is represented by its Stroke. A Stroke is defined as a combination of:

      - A Catmull-Rom spline in the form of a sequence of data points (mandatory), including per-point
        transformational data (optional)
      - Rendering configuration about how the spline should be visualized (optional)
      - Reference to raw input data (SensorData instance), which the path originates from (optional)

  ## Parameters

  sid: `UUID`
  Stroke unique identifier
  `sensor_data_offset` :`int`
    Index of points mapping between raw and processed paths.
  `sensor_data_id` :`UUID`
    Reference UUID of sensor data.
  `sensor_data_mapping` :`list`
    Explicit mapping between indices of Path and SensorData, used when input rate is very high and provides unwanted points.
  `style` :`Style`
    The Style object associated with this stroke.
  `random_seed` :`int`
    A random seed used for randomly generated attributes of a stroke.
  `spline` :`Spline`
    The stroke's Catmull-Rom spline.

  ## Examples

      >>> from uim.model.inkdata.brush import BrushPolygon, BrushPolygonUri, RasterBrush, RotationMode, BlendMode
      >>> from uim.model.inkdata.strokes import Spline, Style, Stroke, LayoutMask
      >>>
      >>> raster_brush_0: RasterBrush = RasterBrush(
      >>> name="app://qa-test-app/raster-brush/MyRasterBrush",
      >>> spacing=10., scattering=5., rotation=RotationMode.TRAJECTORY, shape_textures=[bytes([10, 20]),
      >>>                                                                               bytes([30, 20])],
      >>> fill_width=2.0, fill_height=0.3,
      >>> fill_texture=bytes([10, 10, 20, 15, 17, 20, 25, 16, 34, 255, 23, 0, 34, 255, 23, 255]),
      >>> randomize_fill=False, blend_mode=BlendMode.SOURCE_OVER)
      >>> # Create a spline object - 9 data points, each consisting of X, Y, Size, Red, Green, Blue, Alpha
      >>> spline_1: Spline = Spline(
      >>>     LayoutMask.X.value | LayoutMask.Y.value | LayoutMask.SIZE.value | LayoutMask.RED.value | LayoutMask.GREEN.value
      >>>     | LayoutMask.BLUE.value | LayoutMask.ALPHA.value,
      >>>     [10.0, 10.7, 1.0, 0.5, 0.0, 0.1, 1.0,
      >>>      21.0, 20.2, 2.0, 0.9, 0.4, 0.2, 0.8,
      >>>      30.0, 12.4, 2.1, 0.7, 0.1, 0.1, 0.7,
      >>>      40.0, 15.0, 1.5, 0.3, 0.5, 0.4, 1.0,
      >>>      50.0, 45.0, 1.0, 0.3, 0.5, 0.4, 1.0,
      >>>      41.0, 53.0, 1.1, 0.2, 0.3, 0.5, 0.9,
      >>>      33.0, 73.0, 1.2, 0.6, 0.7, 0.4, 0.8,
      >>>      20.0, 84.0, 1.3, 0.7, 0.8, 0.3, 0.7,
      >>>      10.0, 91.0, 1.1, 0.7, 0.9, 0.2, 0.6]
      >>> )
      >>>
      >>> # Create a style
      >>> style_1: Style = Style(brush_uri=raster_brush_0.name)
      >>> style_1.path_point_properties.rotation = 0.35
      >>>
      >>> # The render mode URI can also be app specific like app://blabla
      >>> # The URI will://rasterization/3.0/blend-mode/SourceOver is assumed and must not be set.
      >>> style_1.render_mode_uri = "will://rasterization/3.0/blend-mode/DestinationOver"
      >>>
      >>> # Create a stroke object. Note that it just exists, but is not in the model yet.
      >>> stroke_1: Stroke = Stroke(UUIDIdentifier.id_generator(), spline=spline_1, style=style_1)

  Expand source code

      class Stroke(UUIDIdentifier):
          """
          Stroke Geometry
          ---------------
          The geometry of an ink stroke is represented by its Stroke.
          A Stroke is defined as a combination of:

              - A Catmull-Rom spline in the form of a sequence of data points (mandatory), including per-point
                transformational data (optional)
              - Rendering configuration about how the spline should be visualized (optional)
              - Reference to raw input data (SensorData instance), which the path originates from (optional)

          Parameters
          ----------
           sid: `UUID`
              Stroke unique identifier
          sensor_data_offset: `int`
              Index of points mapping between raw and processed paths.
          sensor_data_id: `UUID`
              Reference UUID of sensor data.
          sensor_data_mapping: list
              Explicit mapping between indices of Path and SensorData, used when input rate is very high and
              provides unwanted points.
          style: `Style`
              The Style object associated with this stroke.
          random_seed: `int`
              A random seed used for randomly generated attributes of a stroke.
          spline: `Spline`
              The stroke's Catmull-Rom spline.

          Examples
          --------
          >>> from uim.model.inkdata.brush import BrushPolygon, BrushPolygonUri, RasterBrush, RotationMode, BlendMode
          >>> from uim.model.inkdata.strokes import Spline, Style, Stroke, LayoutMask
          >>>
          >>> raster_brush_0: RasterBrush = RasterBrush(
          >>> name="app://qa-test-app/raster-brush/MyRasterBrush",
          >>> spacing=10., scattering=5., rotation=RotationMode.TRAJECTORY, shape_textures=[bytes([10, 20]),
          >>>                                                                               bytes([30, 20])],
          >>> fill_width=2.0, fill_height=0.3,
          >>> fill_texture=bytes([10, 10, 20, 15, 17, 20, 25, 16, 34, 255, 23, 0, 34, 255, 23, 255]),
          >>> randomize_fill=False, blend_mode=BlendMode.SOURCE_OVER)
          >>> # Create a spline object - 9 data points, each consisting of X, Y, Size, Red, Green, Blue, Alpha
          >>> spline_1: Spline = Spline(
          >>>     LayoutMask.X.value | LayoutMask.Y.value | LayoutMask.SIZE.value | LayoutMask.RED.value | LayoutMask.GREEN.value
          >>>     | LayoutMask.BLUE.value | LayoutMask.ALPHA.value,
          >>>     [10.0, 10.7, 1.0, 0.5, 0.0, 0.1, 1.0,
          >>>      21.0, 20.2, 2.0, 0.9, 0.4, 0.2, 0.8,
          >>>      30.0, 12.4, 2.1, 0.7, 0.1, 0.1, 0.7,
          >>>      40.0, 15.0, 1.5, 0.3, 0.5, 0.4, 1.0,
          >>>      50.0, 45.0, 1.0, 0.3, 0.5, 0.4, 1.0,
          >>>      41.0, 53.0, 1.1, 0.2, 0.3, 0.5, 0.9,
          >>>      33.0, 73.0, 1.2, 0.6, 0.7, 0.4, 0.8,
          >>>      20.0, 84.0, 1.3, 0.7, 0.8, 0.3, 0.7,
          >>>      10.0, 91.0, 1.1, 0.7, 0.9, 0.2, 0.6]
          >>> )
          >>>
          >>> # Create a style
          >>> style_1: Style = Style(brush_uri=raster_brush_0.name)
          >>> style_1.path_point_properties.rotation = 0.35
          >>>
          >>> # The render mode URI can also be app specific like app://blabla
          >>> # The URI will://rasterization/3.0/blend-mode/SourceOver is assumed and must not be set.
          >>> style_1.render_mode_uri = "will://rasterization/3.0/blend-mode/DestinationOver"
          >>>
          >>> # Create a stroke object. Note that it just exists, but is not in the model yet.
          >>> stroke_1: Stroke = Stroke(UUIDIdentifier.id_generator(), spline=spline_1, style=style_1)
          """

          def __init__(self, sid: uuid.UUID = None, sensor_data_offset: int = None, sensor_data_id: uuid.UUID = None,
                       sensor_data_mapping: list = None, style: Style = None, random_seed: int = 0, property_index: int = 0,
                       spline: Spline = None):
              super().__init__(sid)
              self.__start_parameter: float = 0.
              self.__end_parameter: float = 0.
              self.__spline_x: List[float] = []
              self.__spline_y: List[float] = []
              self.__spline_z: List[float] = []
              self.__size: List[float] = []
              self.__rotation: List[float] = []
              self.__scale_x: List[float] = []
              self.__scale_y: List[float] = []
              self.__scale_z: List[float] = []
              self.__offset_x: List[float] = []
              self.__offset_y: List[float] = []
              self.__offset_z: List[float] = []
              self.__red: List[int] = []
              self.__green: List[int] = []
              self.__blue: List[int] = []
              self.__alpha: List[int] = []
              self.__tangent_x: List[float] = []
              self.__tangent_y: List[float] = []
              self.__sensor_data_id: uuid.UUID = sensor_data_id
              self.__sensor_data_offset: int = sensor_data_offset or 0
              self._sensor_data_mapping: list = sensor_data_mapping or []
              self.__style: Style = style
              self.__random_seed: int = random_seed
              self.__properties_index: int = property_index
              self.__timestamp_cache = None
              self.__pressure_cache = None
              self.__precision_scheme: Optional[PrecisionScheme] = None
              if spline is not None:
                  self.__import__(spline)

          def set_timestamp_values(self, timestamp_values):
              self.__timestamp_cache = timestamp_values

          def get_timestamp_values(self):
              return self.__timestamp_cache

          def set_pressure_values(self, pressure_values):
              self.__pressure_cache = pressure_values

          def get_pressure_values(self):
              return self.__pressure_cache

          @property
          def layout_mask(self) -> int:
              """Layout mask for the stroke. (`int`)"""
              mask: int = 0
              if len(self.__spline_x) > 0:
                  mask |= LayoutMask.X.value
              if len(self.__spline_y) > 0:
                  mask |= LayoutMask.Y.value
              if len(self.__spline_z) > 0:
                  mask |= LayoutMask.Z.value
              if len(self.__size) > 0:
                  mask |= LayoutMask.SIZE.value
              if len(self.__rotation) > 0:
                  mask |= LayoutMask.ROTATION.value
              if len(self.__red) > 0:
                  mask |= LayoutMask.RED.value
              if len(self.__green) > 0:
                  mask |= LayoutMask.GREEN.value
              if len(self.__blue) > 0:
                  mask |= LayoutMask.BLUE.value
              if len(self.__alpha) > 0:
                  mask |= LayoutMask.ALPHA.value
              if len(self.__scale_x) > 0:
                  mask |= LayoutMask.SCALE_X.value
              if len(self.__scale_y) > 0:
                  mask |= LayoutMask.SCALE_Y.value
              if len(self.__scale_z) > 0:
                  mask |= LayoutMask.SCALE_Z.value
              if len(self.__offset_x) > 0:
                  mask |= LayoutMask.OFFSET_X.value
              if len(self.__offset_y) > 0:
                  mask |= LayoutMask.OFFSET_Y.value
              if len(self.__offset_z) > 0:
                  mask |= LayoutMask.OFFSET_Z.value
              if len(self.__tangent_x) > 0:
                  mask |= LayoutMask.TANGENT_X.value
              if len(self.__tangent_y) > 0:
                  mask |= LayoutMask.TANGENT_Y.value
              return mask

          @property
          def properties_index(self) -> int:
              """Setting the properties index. (`int`)"""
              return self.__properties_index

          @properties_index.setter
          def properties_index(self, properties_index: int):
              self.__properties_index = properties_index

          @property
          def start_parameter(self) -> float:
              """Start parameter of the stroke. (`float`)"""
              return self.__start_parameter

          @start_parameter.setter
          def start_parameter(self, s: float):
              self.__start_parameter = s

          @property
          def end_parameter(self) -> float:
              """End parameter of the stroke. (`float`)"""
              return self.__end_parameter

          @end_parameter.setter
          def end_parameter(self, e: float):
              self.__end_parameter = e

          @property
          def precision_scheme(self) -> PrecisionScheme:
              """An object that defines the precisions used for storing the stroke data. If set to None, stroke data is stored
              using floating point values. (`PrecisionScheme`)"""
              return self.__precision_scheme

          @precision_scheme.setter
          def precision_scheme(self, precision_scheme: PrecisionScheme):
              self.__precision_scheme = precision_scheme

          @property
          def sizes(self) -> List[float]:
              """List of size values. (`List[float]`)"""
              return self.__size

          @sizes.setter
          def sizes(self, size: list):
              self.__size = size

          @property
          def red(self) -> List[int]:
              """List of color values [0, 255] for red channel. (`List[int]`)"""
              return self.__red

          @red.setter
          def red(self, values: List[int]):
              self.__red = values

          @property
          def green(self) -> List[int]:
              """Color values [0, 255] for green channel. (`List[int]`)"""
              return self.__green

          @green.setter
          def green(self, values: List[int]):
              self.__green = values

          @property
          def blue(self) -> List[int]:
              """Color values [0, 255] for blue channel. (`List[int]`)"""
              return self.__blue

          @blue.setter
          def blue(self, values: List[int]):
              self.__blue = values

          @property
          def alpha(self) -> List[int]:
              """Color values [0, 255] for alpha channel. (`List[int]`)"""
              return self.__alpha

          @alpha.setter
          def alpha(self, values: List[int]):
              self.__alpha = values

          @property
          def rotations(self) -> List[float]:
              """List of rotations. (`List[float]`)"""
              return self.__rotation

          @rotations.setter
          def rotations(self, values: List[float]):
              self.__rotation = values

          @property
          def splines_x(self) -> List[float]:
              """List of splines x. (`List[float]`)"""
              return self.__spline_x

          @splines_x.setter
          def splines_x(self, spline_x: List[float]):
              self.__spline_x = spline_x

          @property
          def splines_y(self) -> List[float]:
              """List of splines y. (`List[float]`)"""
              return self.__spline_y

          @splines_y.setter
          def splines_y(self, spline_y: List[float]):
              self.__spline_y = spline_y

          @property
          def splines_z(self) -> List[float]:
              """List of splines z. (`List[float]`)"""
              return self.__spline_z

          @splines_z.setter
          def splines_z(self, spline_z: List[float]):
              self.__spline_z = spline_z

          @property
          def scales_x(self) -> List[float]:
              """List of x scales. (`List[float]`)"""
              return self.__scale_x

          @scales_x.setter
          def scales_x(self, scale: List[float]):
              self.__scale_x = scale

          @property
          def scales_y(self) -> List[float]:
              """List of y scales. (`List[float]`)"""
              return self.__scale_y

          @scales_y.setter
          def scales_y(self, scale: List[float]):
              self.__scale_y = scale

          @property
          def scales_z(self) -> List[float]:
              """List of z scales. (`List[float]`)"""
              return self.__scale_z

          @scales_z.setter
          def scales_z(self, scale: list):
              self.__scale_z = scale

          @property
          def offsets_x(self) -> List[float]:
              """List of x offsets."""
              return self.__offset_x

          @offsets_x.setter
          def offsets_x(self, offset: List[float]):
              self.__offset_x = offset

          @property
          def offsets_y(self) -> List[float]:
              """List of y offsets. (`List[float]`)"""
              return self.__offset_y

          @offsets_y.setter
          def offsets_y(self, offset: List[float]):
              self.__offset_y = offset

          @property
          def offsets_z(self) -> List[float]:
              """List of z offsets. (`List[float]`)"""
              return self.__offset_z

          @offsets_z.setter
          def offsets_z(self, offset: List[float]):
              self.__offset_z = offset

          @property
          def sensor_data_offset(self) -> int:
              """Index of points mapping between raw and processed paths. (`int`)"""
              return self.__sensor_data_offset

          @property
          def sensor_data_id(self) -> uuid.UUID:
              """Reference id sensor data. (`UUID`)"""
              return self.__sensor_data_id

          @property
          def sensor_data_mapping(self) -> List[int]:
              """Explicit mapping between indices of Path and SensorData, used when input rate is very high and
              provides unwanted points. (`List[int]`)"""
              return self._sensor_data_mapping

          @property
          def style(self) -> Style:
              """Style that is applied to the path of the stroke. (`Style`)"""
              return self.__style

          @style.setter
          def style(self, style: Style):
              self.__style = style

          @property
          def spline_min_x(self) -> float:
              """Minimum value of x spline. (`float`)"""
              return np.min(self.__spline_x)

          @property
          def spline_min_y(self) -> float:
              """Minimum value of y spline. (`float`)"""
              return np.min(self.__spline_y)

          @property
          def spline_max_x(self) -> float:
              """Maximum value of x spline. (`float`)"""
              return np.max(self.__spline_x)

          @property
          def spline_max_y(self) -> float:
              """Maximum value of y spline. (`float`)"""
              return np.max(self.__spline_y)

          @property
          def points_count(self) -> int:
              """Number of points of sample points. (`int`)"""
              return len(self.__spline_x)

          @property
          def bounding_box(self) -> BoundingBox:
              """Bounding box for path stroke. (`BoundingBox`)"""
              x_min = self.spline_min_x
              x_max = self.spline_max_x
              y_min = self.spline_min_y
              y_max = self.spline_max_y
              return BoundingBox(x=x_min, y=y_min, width=x_max - x_min, height=y_max - y_min)

          def __import__(self, spline: Spline):
              # The content from spline is imported with the appropriate Layout mask being set.
              self.__start_parameter = spline.ts
              self.__end_parameter = spline.tf
              idx: int = 0
              while idx < len(spline.data):
                  if spline.layout_mask & LayoutMask.X.value:
                      self.__spline_x.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.Y.value:
                      self.__spline_y.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.Z.value:
                      self.__spline_z.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.SIZE.value:
                      self.__size.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.ROTATION.value:
                      self.__rotation.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.RED.value:
                      self.__red.append(int(spline.data[idx] * 255))
                      idx += 1
                  if spline.layout_mask & LayoutMask.GREEN.value:
                      self.__green.append(int(spline.data[idx] * 255))
                      idx += 1
                  if spline.layout_mask & LayoutMask.BLUE.value:
                      self.__blue.append(int(spline.data[idx] * 255))
                      idx += 1
                  if spline.layout_mask & LayoutMask.ALPHA.value:
                      self.__alpha.append(int(spline.data[idx] * 255))
                      idx += 1
                  if spline.layout_mask & LayoutMask.SCALE_X.value:
                      self.__scale_x.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.SCALE_Y.value:
                      self.__scale_y.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.SCALE_Z.value:
                      self.__scale_z.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.OFFSET_X.value:
                      self.__offset_x.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.OFFSET_Y.value:
                      self.__offset_y.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.OFFSET_Z.value:
                      self.__offset_z.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.TANGENT_X.value:
                      self.__tangent_x.append(spline.data[idx])
                      idx += 1
                  if spline.layout_mask & LayoutMask.TANGENT_Y.value:
                      self.__tangent_y.append(spline.data[idx])
                      idx += 1

          def __repr__(self):
              return f'<Stroke : [id:={self.id_h_form}], [num points:={self.points_count}, layout mask:={self.layout_mask}]>'

  ### Ancestors

  - UUIDIdentifier
  - Identifier
  - abc.ABC

  ### Class variables

  `var SEPARATOR : str`

  ### Instance variables

  `var alpha : List[int]`

    Color values [0, 255] for alpha channel. (`List[int]`)

    Expand source code

        @property
        def alpha(self) -> List[int]:
            """Color values [0, 255] for alpha channel. (`List[int]`)"""
            return self.__alpha

  `var blue : List[int]`

    Color values [0, 255] for blue channel. (`List[int]`)

    Expand source code

        @property
        def blue(self) -> List[int]:
            """Color values [0, 255] for blue channel. (`List[int]`)"""
            return self.__blue

  `var bounding_box : BoundingBox`

    Bounding box for path stroke. (`BoundingBox`)

    Expand source code

        @property
        def bounding_box(self) -> BoundingBox:
            """Bounding box for path stroke. (`BoundingBox`)"""
            x_min = self.spline_min_x
            x_max = self.spline_max_x
            y_min = self.spline_min_y
            y_max = self.spline_max_y
            return BoundingBox(x=x_min, y=y_min, width=x_max - x_min, height=y_max - y_min)

  `var end_parameter : float`

    End parameter of the stroke. (`float`)

    Expand source code

        @property
        def end_parameter(self) -> float:
            """End parameter of the stroke. (`float`)"""
            return self.__end_parameter

  `var green : List[int]`

    Color values [0, 255] for green channel. (`List[int]`)

    Expand source code

        @property
        def green(self) -> List[int]:
            """Color values [0, 255] for green channel. (`List[int]`)"""
            return self.__green

  `var layout_mask : int`

    Layout mask for the stroke. (`int`)

    Expand source code

        @property
        def layout_mask(self) -> int:
            """Layout mask for the stroke. (`int`)"""
            mask: int = 0
            if len(self.__spline_x) > 0:
                mask |= LayoutMask.X.value
            if len(self.__spline_y) > 0:
                mask |= LayoutMask.Y.value
            if len(self.__spline_z) > 0:
                mask |= LayoutMask.Z.value
            if len(self.__size) > 0:
                mask |= LayoutMask.SIZE.value
            if len(self.__rotation) > 0:
                mask |= LayoutMask.ROTATION.value
            if len(self.__red) > 0:
                mask |= LayoutMask.RED.value
            if len(self.__green) > 0:
                mask |= LayoutMask.GREEN.value
            if len(self.__blue) > 0:
                mask |= LayoutMask.BLUE.value
            if len(self.__alpha) > 0:
                mask |= LayoutMask.ALPHA.value
            if len(self.__scale_x) > 0:
                mask |= LayoutMask.SCALE_X.value
            if len(self.__scale_y) > 0:
                mask |= LayoutMask.SCALE_Y.value
            if len(self.__scale_z) > 0:
                mask |= LayoutMask.SCALE_Z.value
            if len(self.__offset_x) > 0:
                mask |= LayoutMask.OFFSET_X.value
            if len(self.__offset_y) > 0:
                mask |= LayoutMask.OFFSET_Y.value
            if len(self.__offset_z) > 0:
                mask |= LayoutMask.OFFSET_Z.value
            if len(self.__tangent_x) > 0:
                mask |= LayoutMask.TANGENT_X.value
            if len(self.__tangent_y) > 0:
                mask |= LayoutMask.TANGENT_Y.value
            return mask

  `var offsets_x : List[float]`

    List of x offsets.

    Expand source code

        @property
        def offsets_x(self) -> List[float]:
            """List of x offsets."""
            return self.__offset_x

  `var offsets_y : List[float]`

    List of y offsets. (`List[float]`)

    Expand source code

        @property
        def offsets_y(self) -> List[float]:
            """List of y offsets. (`List[float]`)"""
            return self.__offset_y

  `var offsets_z : List[float]`

    List of z offsets. (`List[float]`)

    Expand source code

        @property
        def offsets_z(self) -> List[float]:
            """List of z offsets. (`List[float]`)"""
            return self.__offset_z

  `var points_count : int`

    Number of points of sample points. (`int`)

    Expand source code

        @property
        def points_count(self) -> int:
            """Number of points of sample points. (`int`)"""
            return len(self.__spline_x)

  `var precision_scheme : PrecisionScheme`

    An object that defines the precisions used for storing the stroke data. If set to None, stroke data is stored using floating point values. (`PrecisionScheme`)

    Expand source code

        @property
        def precision_scheme(self) -> PrecisionScheme:
            """An object that defines the precisions used for storing the stroke data. If set to None, stroke data is stored
            using floating point values. (`PrecisionScheme`)"""
            return self.__precision_scheme

  `var properties_index : int`

    Setting the properties index. (`int`)

    Expand source code

        @property
        def properties_index(self) -> int:
            """Setting the properties index. (`int`)"""
            return self.__properties_index

  `var red : List[int]`

    List of color values [0, 255] for red channel. (`List[int]`)

    Expand source code

        @property
        def red(self) -> List[int]:
            """List of color values [0, 255] for red channel. (`List[int]`)"""
            return self.__red

  `var rotations : List[float]`

    List of rotations. (`List[float]`)

    Expand source code

        @property
        def rotations(self) -> List[float]:
            """List of rotations. (`List[float]`)"""
            return self.__rotation

  `var scales_x : List[float]`

    List of x scales. (`List[float]`)

    Expand source code

        @property
        def scales_x(self) -> List[float]:
            """List of x scales. (`List[float]`)"""
            return self.__scale_x

  `var scales_y : List[float]`

    List of y scales. (`List[float]`)

    Expand source code

        @property
        def scales_y(self) -> List[float]:
            """List of y scales. (`List[float]`)"""
            return self.__scale_y

  `var scales_z : List[float]`

    List of z scales. (`List[float]`)

    Expand source code

        @property
        def scales_z(self) -> List[float]:
            """List of z scales. (`List[float]`)"""
            return self.__scale_z

  `var sensor_data_id : uuid.UUID`

    Reference id sensor data. (`UUID`)

    Expand source code

        @property
        def sensor_data_id(self) -> uuid.UUID:
            """Reference id sensor data. (`UUID`)"""
            return self.__sensor_data_id

  `var sensor_data_mapping : List[int]`

    Explicit mapping between indices of Path and SensorData, used when input rate is very high and provides unwanted points. (`List[int]`)

    Expand source code

        @property
        def sensor_data_mapping(self) -> List[int]:
            """Explicit mapping between indices of Path and SensorData, used when input rate is very high and
            provides unwanted points. (`List[int]`)"""
            return self._sensor_data_mapping

  `var sensor_data_offset : int`

    Index of points mapping between raw and processed paths. (`int`)

    Expand source code

        @property
        def sensor_data_offset(self) -> int:
            """Index of points mapping between raw and processed paths. (`int`)"""
            return self.__sensor_data_offset

  `var sizes : List[float]`

    List of size values. (`List[float]`)

    Expand source code

        @property
        def sizes(self) -> List[float]:
            """List of size values. (`List[float]`)"""
            return self.__size

  `var spline_max_x : float`

    Maximum value of x spline. (`float`)

    Expand source code

        @property
        def spline_max_x(self) -> float:
            """Maximum value of x spline. (`float`)"""
            return np.max(self.__spline_x)

  `var spline_max_y : float`

    Maximum value of y spline. (`float`)

    Expand source code

        @property
        def spline_max_y(self) -> float:
            """Maximum value of y spline. (`float`)"""
            return np.max(self.__spline_y)

  `var spline_min_x : float`

    Minimum value of x spline. (`float`)

    Expand source code

        @property
        def spline_min_x(self) -> float:
            """Minimum value of x spline. (`float`)"""
            return np.min(self.__spline_x)

  `var spline_min_y : float`

    Minimum value of y spline. (`float`)

    Expand source code

        @property
        def spline_min_y(self) -> float:
            """Minimum value of y spline. (`float`)"""
            return np.min(self.__spline_y)

  `var splines_x : List[float]`

    List of splines x. (`List[float]`)

    Expand source code

        @property
        def splines_x(self) -> List[float]:
            """List of splines x. (`List[float]`)"""
            return self.__spline_x

  `var splines_y : List[float]`

    List of splines y. (`List[float]`)

    Expand source code

        @property
        def splines_y(self) -> List[float]:
            """List of splines y. (`List[float]`)"""
            return self.__spline_y

  `var splines_z : List[float]`

    List of splines z. (`List[float]`)

    Expand source code

        @property
        def splines_z(self) -> List[float]:
            """List of splines z. (`List[float]`)"""
            return self.__spline_z

  `var start_parameter : float`

    Start parameter of the stroke. (`float`)

    Expand source code

        @property
        def start_parameter(self) -> float:
            """Start parameter of the stroke. (`float`)"""
            return self.__start_parameter

  `var style : Style`

    Style that is applied to the path of the stroke. (`Style`)

    Expand source code

        @property
        def style(self) -> Style:
            """Style that is applied to the path of the stroke. (`Style`)"""
            return self.__style

  ### Methods

  `def get_pressure_values(self)`
    Expand source code

        def get_pressure_values(self):
            return self.__pressure_cache

  `def get_timestamp_values(self)`
    Expand source code

        def get_timestamp_values(self):
            return self.__timestamp_cache

  `def set_pressure_values(self, pressure_values)`
    Expand source code

        def set_pressure_values(self, pressure_values):
            self.__pressure_cache = pressure_values

  `def set_timestamp_values(self, timestamp_values)`
    Expand source code

        def set_timestamp_values(self, timestamp_values):
            self.__timestamp_cache = timestamp_values

  ### Inherited members

  - `UUIDIdentifier`:
    - `from_bytes`
    - `id`
    - `id_generator`
    - `id_h_form`
    - `id_s_form`
    - `str_to_uimid`
    - `uimid_to_h_form`
    - `uimid_to_s_form`
`class Style (properties: PathPointProperties = None, brush_uri: str = None, particles_random_seed: int = 0, render_mode_uri: str = 'will://rasterization/3.0/blend-mode/SourceOver')`

  # Style

  The `Style` is defined as a combination of a `PathPointProperties` configuration, reference to a Brush, a random number generator seed value and rendering method type. Setting the Style property allows overriding of specific path point properties, color components and/or matrix transformational components. A `Style` with `PathPointProperties` configuration should be normally used to define constant path components.

  ## Parameters

  `properties` :`PathPointProperties`
    Static values of properties which do not exist per per path point
  `brush_uri` :`str`
    Reference to Brush used for stroke rasterization
  `particles_random_seed` :`int`
    Particles random seed, required for particle strokes
  `render_mode_uri` :`str`
    Render mode URI
  Expand source code

      class Style(ABC):
          """
          Style
          =====
          The `Style` is defined as a combination of a `PathPointProperties` configuration, reference to a Brush,
          a random number generator seed value and rendering method type. Setting the Style property allows overriding of
          specific path point properties, color components and/or matrix transformational components.
          A `Style` with `PathPointProperties` configuration should be normally used to define constant path components.

          Parameters
          ----------
          properties: `PathPointProperties`
              Static values of properties which do not exist per per path point
          brush_uri: str
              Reference to Brush used for stroke rasterization
          particles_random_seed: int
              Particles random seed, required for particle strokes
          render_mode_uri: str
              Render mode URI
          """

          def __init__(self, properties: PathPointProperties = None, brush_uri: str = None, particles_random_seed: int = 0,
                       render_mode_uri: str = BlendModeURIs.SOURCE_OVER):
              self.__properties = properties if properties is not None else PathPointProperties()
              self.__brush_uri = brush_uri
              self.__particles_random_seed = particles_random_seed
              self.__render_mode_URI = render_mode_uri if render_mode_uri is not None and render_mode_uri != '' \
                  else BlendModeURIs.SOURCE_OVER

          @property
          def path_point_properties(self) -> PathPointProperties:
              """Static values of properties which do not exist per per path point. (`PathPointProperties`, read-only)"""
              return self.__properties

          @property
          def brush_uri(self) -> str:
              """Reference to Brush used for stroke rasterization using the URI. (`str`, read-only)"""
              return self.__brush_uri

          @property
          def particles_random_seed(self):
              """Particles random seed, required for particle strokes.  (`int`, read-only) """
              return self.__particles_random_seed

          @property
          def render_mode_uri(self):
              """Defines additional information about stroke visualisation, such as ERASER. (`str`)"""
              return self.__render_mode_URI

          @render_mode_uri.setter
          def render_mode_uri(self, uri: str):
              self.__render_mode_URI = uri

          def __repr__(self):
              return '<Style : [id:={}, particles_random_seed:={}, render mode:={}>' \
                  .format(self.__brush_uri,
                          self.__particles_random_seed,
                          self.__render_mode_URI)

  ### Ancestors

  - abc.ABC

  ### Instance variables

  `var brush_uri : str`

    Reference to Brush used for stroke rasterization using the URI. (`str`, read-only)

    Expand source code

        @property
        def brush_uri(self) -> str:
            """Reference to Brush used for stroke rasterization using the URI. (`str`, read-only)"""
            return self.__brush_uri

  `var particles_random_seed`

    Particles random seed, required for particle strokes. (`int`, read-only)

    Expand source code

        @property
        def particles_random_seed(self):
            """Particles random seed, required for particle strokes.  (`int`, read-only) """
            return self.__particles_random_seed

  `var path_point_properties : PathPointProperties`

    Static values of properties which do not exist per per path point. (`PathPointProperties`, read-only)

    Expand source code

        @property
        def path_point_properties(self) -> PathPointProperties:
            """Static values of properties which do not exist per per path point. (`PathPointProperties`, read-only)"""
            return self.__properties

  `var render_mode_uri`

    Defines additional information about stroke visualisation, such as ERASER. (`str`)

    Expand source code

        @property
        def render_mode_uri(self):
            """Defines additional information about stroke visualisation, such as ERASER. (`str`)"""
            return self.__render_mode_URI