vmcp.typing

typing module of vmcp package.

  1#!/usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3# SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5"""``typing`` module of ``vmcp`` package."""
  6
  7from typing import overload
  8from dataclasses import dataclass
  9from enum import Enum
 10from math import (
 11    cos,
 12    sin,
 13    atan2,
 14    asin,
 15    sqrt
 16)
 17from time import time
 18
 19
 20# Availability
 21class ModelState(Enum):
 22    """VMC model state."""
 23
 24    NOT_LOADED = 0
 25    LOADED = 1
 26
 27
 28class CalibrationState(Enum):
 29    """VMC calibration state."""
 30
 31    UNCALIBRATED = 0
 32    WAITING_FOR_CALIBRATING = 1
 33    CALIBRATING = 2
 34    CALIBRATED = 3
 35
 36
 37class CalibrationMode(Enum):
 38    """VMC calibration mode."""
 39
 40    NORMAL = 0
 41    MR_HAND = 1
 42    MR_FLOOR = 2
 43
 44
 45class TrackingState(Enum):
 46    """VMC tracking state."""
 47
 48    BAD = 0
 49    OK = 1
 50
 51
 52# Devices
 53class DeviceType(str, Enum):
 54    """VMC device types."""
 55
 56    HMD = "Hmd"
 57    CONTROLLER = "Con"
 58    TRACKER = "Tra"
 59
 60
 61# 3D Space
 62class Bone(str, Enum):
 63    """All 55 HumanBodyBones from Unity."""
 64
 65    HIPS = "Hips"
 66    LEFT_UPPER_LEG = "LeftUpperLeg"
 67    RIGHT_UPPER_LEG = "RightUpperLeg"
 68    LEFT_LOWER_LEG = "LeftLowerLeg"
 69    RIGHT_LOWER_LEG = "RightLowerLeg"
 70    LEFT_FOOT = "LeftFoot"
 71    RIGHT_FOOT = "RightFoot"
 72    SPINE = "Spine"
 73    CHEST = "Chest"
 74    UPPER_CHEST = "UpperChest"
 75    NECK = "Neck"
 76    HEAD = "Head"
 77    LEFT_SHOULDER = "LeftShoulder"
 78    RIGHT_SHOULDER = "RightShoulder"
 79    LEFT_UPPER_ARM = "LeftUpperArm"
 80    RIGHT_UPPER_ARM = "RightUpperArm"
 81    LEFT_LOWER_ARM = "LeftLowerArm"
 82    RIGHT_LOWER_ARM = "RightLowerArm"
 83    LEFT_HAND = "LeftHand"
 84    RIGHT_HAND = "RightHand"
 85    LEFT_TOES = "LeftToes"
 86    RIGHT_TOES = "RightToes"
 87    LEFT_EYE = "LeftEye"
 88    RIGHT_EYE = "RightEye"
 89    JAW = "Jaw"
 90    LEFT_THUMB_PROXIMAL = "LeftThumbProximal"
 91    LEFT_THUMB_INTERMEDIATE = "LeftThumbIntermediate"
 92    LEFT_THUMB_DISTAL = "LeftThumbDistal"
 93    LEFT_INDEX_PROXIMAL = "LeftIndexProximal"
 94    LEFT_INDEX_INTERMEDIATE = "LeftIndexIntermediate"
 95    LEFT_INDEX_DISTAL = "LeftIndexDistal"
 96    LEFT_MIDDLE_PROXIMAL = "LeftMiddleProximal"
 97    LEFT_MIDDLE_INTERMEDIATE = "LeftMiddleIntermediate"
 98    LEFT_MIDDLE_DISTAL = "LeftMiddleDistal"
 99    LEFT_RING_PROXIMAL = "LeftRingProximal"
100    LEFT_RING_INTERMEDIATE = "LeftRingIntermediate"
101    LEFT_RING_DISTAL = "LeftRingDistal"
102    LEFT_LITTLE_PROXIMAL = "LeftLittleProximal"
103    LEFT_LITTLE_INTERMEDIATE = "LeftLittleIntermediate"
104    LEFT_LITTLE_DISTAL = "LeftLittleDistal"
105    RIGHT_THUMB_PROXIMAL = "RightThumbProximal"
106    RIGHT_THUMB_INTERMEDIATE = "RightThumbIntermediate"
107    RIGHT_THUMB_DISTAL = "RightThumbDistal"
108    RIGHT_INDEX_PROXIMAL = "RightIndexProximal"
109    RIGHT_INDEX_INTERMEDIATE = "RightIndexIntermediate"
110    RIGHT_INDEX_DISTAL = "RightIndexDistal"
111    RIGHT_MIDDLE_PROXIMAL = "RightMiddleProximal"
112    RIGHT_MIDDLE_INTERMEDIATE = "RightMiddleIntermediate"
113    RIGHT_MIDDLE_DISTAL = "RightMiddleDistal"
114    RIGHT_RING_PROXIMAL = "RightRingProximal"
115    RIGHT_RING_INTERMEDIATE = "RightRingIntermediate"
116    RIGHT_RING_DISTAL = "RightRingDistal"
117    RIGHT_LITTLE_PROXIMAL = "RightLittleProximal"
118    RIGHT_LITTLE_INTERMEDIATE = "RightLittleIntermediate"
119    RIGHT_LITTLE_DISTAL = "RightLittleDistal"
120
121
122class BlendShapeKey(str, Enum):
123    """BlendShapeKey interface."""
124
125
126@dataclass(frozen=True, slots=True)
127class CoordinateVector:
128    """Cartesian coordinate vector."""
129
130    x: float  # pylint: disable=invalid-name
131    """float: x coordinate."""
132
133    y: float  # pylint: disable=invalid-name
134    """float: y coordinate."""
135
136    z: float  # pylint: disable=invalid-name
137    """float: z coordinate."""
138
139    @classmethod
140    def identity(cls) -> 'CoordinateVector':
141        """Create an identity position.
142
143        Returns:
144            Position:
145                Created identity position (x, y, z).
146
147        >>> CoordinateVector.identity()
148        CoordinateVector(0.0, 0.0, 0.0)
149
150        """
151        return cls(0.0, 0.0, 0.0)
152
153
154@dataclass(frozen=True, slots=True)
155class Quaternion:
156    """Quaternion."""
157
158    x: float  # pylint: disable=invalid-name
159    """float: x component."""
160
161    y: float  # pylint: disable=invalid-name
162    """float: y component."""
163
164    z: float  # pylint: disable=invalid-name
165    """float: z component."""
166
167    w: float  # pylint: disable=invalid-name
168    """float: w component."""
169
170    @classmethod
171    def identity(cls) -> 'Quaternion':
172        """Create an identity quaternion.
173
174        Returns:
175            Quaternion:
176                Created identity quaternion (x, y, z, w).
177
178        >>> Quaternion.identity()
179        0.0, 0.0, 0.0, 1.0
180
181        """
182        return cls(0.0, 0.0, 0.0, 1.0)
183
184    @classmethod
185    def from_rotvec(
186        cls,
187        x: float,  # pylint: disable=invalid-name
188        y: float,  # pylint: disable=invalid-name
189        z: float  # pylint: disable=invalid-name
190    ) -> 'Quaternion':
191        """Create from rotation vector.
192
193        Args:
194            x (float):
195                x dimension.
196            y (float):
197                y dimension.
198            z (float):
199                z dimension.
200
201        .. _Source:
202            https://github.com/scipy/scipy/blob/main/scipy/spatial/transform/_rotation.pyx#L872-L876
203
204        """
205        angle: float = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2))
206
207        # Stolen from the `scipy` package
208        if angle <= 1e-3:
209            # small angle
210            angle2 = angle * angle
211            scale = 0.5 - angle2 / 48 + angle2 * angle2 / 3840
212        else:
213            # large angle
214            scale = sin(angle / 2) / angle
215
216        return cls(
217            x * scale,
218            y * scale,
219            z * scale,
220            cos(angle / 2)
221        )
222
223    @classmethod
224    def from_euler(
225        cls,
226        phi: float,
227        theta: float,
228        psi: float
229    ) -> 'Quaternion':
230        """Create from euler angles.
231
232        Args:
233            phi (float):
234                Rotation angle around the X axis (radian).
235            theta (float):
236                Rotation angle around the Y axis (radian).
237            psi (float):
238                Rotation angle around the Y axis (radian).
239
240        Returns:
241            Quaternion:
242                Created quaternion (x, y, z, w).
243
244        .. _Source:
245            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
246
247        """
248        # x axis rotation angle
249        cos_phi_half = cos(phi / 2)
250        sin_phi_half = sin(phi / 2)
251        # y axis rotation angle
252        cos_theta_half = cos(theta / 2)
253        sin_theta_half = sin(theta / 2)
254        # z axis rotation angle
255        cos_psi_half = cos(psi / 2)
256        sin_psi_half = sin(psi / 2)
257        # Calculation
258        return cls(
259            x=float(
260                sin_phi_half * cos_theta_half * cos_psi_half -
261                cos_phi_half * sin_theta_half * sin_psi_half
262            ),
263            y=float(
264                cos_phi_half * sin_theta_half * cos_psi_half +
265                sin_phi_half * cos_theta_half * sin_psi_half
266            ),
267            z=float(
268                cos_phi_half * cos_theta_half * sin_psi_half -
269                sin_phi_half * sin_theta_half * cos_psi_half
270            ),
271            w=float(
272                cos_phi_half * cos_theta_half * cos_psi_half +
273                sin_phi_half * sin_theta_half * sin_psi_half
274            )
275        )
276
277    def to_euler(self) -> tuple[float, float, float]:
278        """Convert to euler angles.
279
280        Returns:
281            tuple[float, float, float]:
282                Phi, theta and psi (radian).
283
284        .. _Source:
285            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
286
287        """
288        # x axis rotation angle
289        term0 = 2 * (self.w * self.x + self.y * self.z)
290        term1 = 1 - 2 * (self.x * self.x + self.y * self.y)
291        phi = atan2(term0, term1)
292        # y axis rotation angle
293        term2 = 2 * (self.w * self.y - self.z * self.x)
294        term2 = 1 if term2 > 1 else term2
295        term2 = -1 if term2 < -1 else term2
296        theta = asin(term2)
297        # y axis rotation angle
298        term3 = 2 * (self.w * self.z + self.x * self.y)
299        term4 = 1 - 2 * (self.y * self.y + self.z * self.z)
300        psi = atan2(term3, term4)
301        return phi, theta, psi
302
303    def conjugate(self) -> 'Quaternion':
304        """Return conjugated quaternion.
305
306        Returns:
307            Quaternion:
308                Conjugated quaternion (x, y, z, w).
309
310        .. _Source:
311            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
312
313        """
314        # Calculation
315        return Quaternion(
316            x=-self.x,
317            y=-self.y,
318            z=-self.z,
319            w=self.w
320        )
321
322    def multiply_by(
323        self,
324        quaternion: 'Quaternion'
325    ) -> 'Quaternion':
326        """Multiplicate quaternion with an given one and return the product.
327
328        Returns:
329            Quaternion:
330                Multiplication product quaternion (x, y, z, w)
331
332        .. _Source:
333            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
334
335        """
336        # Calculation
337        return Quaternion(
338            x=float(
339                self.w * quaternion.x +
340                self.x * quaternion.w +
341                self.y * quaternion.z -
342                self.z * quaternion.y
343            ),
344            y=float(
345                self.w * quaternion.y +
346                self.y * quaternion.w +
347                self.z * quaternion.x -
348                self.x * quaternion.z
349            ),
350            z=float(
351                self.w * quaternion.z +
352                self.z * quaternion.w +
353                self.x * quaternion.y -
354                self.y * quaternion.x
355            ),
356            w=float(
357                self.w * quaternion.w -
358                self.x * quaternion.x -
359                self.y * quaternion.y -
360                self.z * quaternion.z
361            )
362        )
363
364
365@dataclass(frozen=False, slots=True, init=False)
366class Scale:
367    """Scale."""
368
369    width: float
370    """float: Width dimension."""
371
372    height: float
373    """float: Height dimension."""
374
375    length: float
376    """float: Length dimension."""
377
378    @overload
379    def __init__(
380        self,
381        uniformly: float
382    ) -> None:
383        """Scale constructor.
384
385        Args:
386            uniformly (float):
387                All dimensions.
388
389        """
390
391    @overload
392    def __init__(
393        self,
394        width: float,
395        height: float,
396        length: float
397    ) -> None:
398        """Scale constructor.
399
400        Args:
401            width (float):
402                Width dimension.
403            height (float):
404                Height dimension.
405            length (float):
406                Length dimension.
407
408        """
409
410    def __init__(
411        self,
412        *args: float,
413        **kwargs: float
414    ) -> None:
415        """Implement scale constructor.
416
417        Args:
418            *args (float):
419                1 OR 3 dimension arguments.
420        Raises:
421            ValueError:
422                If invalid arguments are given.
423
424        """
425        args += tuple(kwargs.values())
426        match len(args):
427            case 1:
428                self.width = self.height = self.length = args[0]
429            case 3:
430                self.width = args[0]
431                self.height = args[1]
432                self.length = args[2]
433            case _:
434                raise ValueError(f"Invalid parameters given: {str(args)}")
435
436
437# Communication
438class Timestamp(float):
439    """Timestamp since the epoch in seconds with it's fractions."""
440
441    def __new__(
442        cls,
443        at: float = None
444    ) -> 'Timestamp':
445        """Create a new instance of class ``Timestamp``.
446
447        Args:
448            at (Optional[float]):
449                Seconds with it's fractions since the epoch.
450
451        Returns:
452            Timestamp:
453                Object instance.
454
455        """
456        return super().__new__(cls, time() if at is None else at)
457
458    def elapsed(self) -> float:
459        """Return elapsed time in seconds with it's fractions as float.
460
461        Returns:
462            float:
463                Elapsed time in seconds with it's fractions.
464
465        >>> Timestamp().elapsed()
466        0.0
467
468        """
469        return time() - self
470
471    def __str__(self) -> str:
472        """Return string representation of the object.
473
474        Returns:
475            str:
476                Representation of the object.
477
478        """
479        return super().__repr__()
480
481    def __repr__(self) -> str:
482        """Return a string, representing the object in a reconstructable way.
483
484        Returns:
485            str:
486                Representing the object in a reconstructable way.
487
488        """
489        return f"{self.__class__.__qualname__}(at={self})"
class ModelState(enum.Enum):
22class ModelState(Enum):
23    """VMC model state."""
24
25    NOT_LOADED = 0
26    LOADED = 1

VMC model state.

NOT_LOADED = <ModelState.NOT_LOADED: 0>
LOADED = <ModelState.LOADED: 1>
Inherited Members
enum.Enum
name
value
class CalibrationState(enum.Enum):
29class CalibrationState(Enum):
30    """VMC calibration state."""
31
32    UNCALIBRATED = 0
33    WAITING_FOR_CALIBRATING = 1
34    CALIBRATING = 2
35    CALIBRATED = 3

VMC calibration state.

UNCALIBRATED = <CalibrationState.UNCALIBRATED: 0>
WAITING_FOR_CALIBRATING = <CalibrationState.WAITING_FOR_CALIBRATING: 1>
CALIBRATING = <CalibrationState.CALIBRATING: 2>
CALIBRATED = <CalibrationState.CALIBRATED: 3>
Inherited Members
enum.Enum
name
value
class CalibrationMode(enum.Enum):
38class CalibrationMode(Enum):
39    """VMC calibration mode."""
40
41    NORMAL = 0
42    MR_HAND = 1
43    MR_FLOOR = 2

VMC calibration mode.

NORMAL = <CalibrationMode.NORMAL: 0>
MR_HAND = <CalibrationMode.MR_HAND: 1>
MR_FLOOR = <CalibrationMode.MR_FLOOR: 2>
Inherited Members
enum.Enum
name
value
class TrackingState(enum.Enum):
46class TrackingState(Enum):
47    """VMC tracking state."""
48
49    BAD = 0
50    OK = 1

VMC tracking state.

BAD = <TrackingState.BAD: 0>
OK = <TrackingState.OK: 1>
Inherited Members
enum.Enum
name
value
class DeviceType(builtins.str, enum.Enum):
54class DeviceType(str, Enum):
55    """VMC device types."""
56
57    HMD = "Hmd"
58    CONTROLLER = "Con"
59    TRACKER = "Tra"

VMC device types.

HMD = <DeviceType.HMD: 'Hmd'>
CONTROLLER = <DeviceType.CONTROLLER: 'Con'>
TRACKER = <DeviceType.TRACKER: 'Tra'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Bone(builtins.str, enum.Enum):
 63class Bone(str, Enum):
 64    """All 55 HumanBodyBones from Unity."""
 65
 66    HIPS = "Hips"
 67    LEFT_UPPER_LEG = "LeftUpperLeg"
 68    RIGHT_UPPER_LEG = "RightUpperLeg"
 69    LEFT_LOWER_LEG = "LeftLowerLeg"
 70    RIGHT_LOWER_LEG = "RightLowerLeg"
 71    LEFT_FOOT = "LeftFoot"
 72    RIGHT_FOOT = "RightFoot"
 73    SPINE = "Spine"
 74    CHEST = "Chest"
 75    UPPER_CHEST = "UpperChest"
 76    NECK = "Neck"
 77    HEAD = "Head"
 78    LEFT_SHOULDER = "LeftShoulder"
 79    RIGHT_SHOULDER = "RightShoulder"
 80    LEFT_UPPER_ARM = "LeftUpperArm"
 81    RIGHT_UPPER_ARM = "RightUpperArm"
 82    LEFT_LOWER_ARM = "LeftLowerArm"
 83    RIGHT_LOWER_ARM = "RightLowerArm"
 84    LEFT_HAND = "LeftHand"
 85    RIGHT_HAND = "RightHand"
 86    LEFT_TOES = "LeftToes"
 87    RIGHT_TOES = "RightToes"
 88    LEFT_EYE = "LeftEye"
 89    RIGHT_EYE = "RightEye"
 90    JAW = "Jaw"
 91    LEFT_THUMB_PROXIMAL = "LeftThumbProximal"
 92    LEFT_THUMB_INTERMEDIATE = "LeftThumbIntermediate"
 93    LEFT_THUMB_DISTAL = "LeftThumbDistal"
 94    LEFT_INDEX_PROXIMAL = "LeftIndexProximal"
 95    LEFT_INDEX_INTERMEDIATE = "LeftIndexIntermediate"
 96    LEFT_INDEX_DISTAL = "LeftIndexDistal"
 97    LEFT_MIDDLE_PROXIMAL = "LeftMiddleProximal"
 98    LEFT_MIDDLE_INTERMEDIATE = "LeftMiddleIntermediate"
 99    LEFT_MIDDLE_DISTAL = "LeftMiddleDistal"
100    LEFT_RING_PROXIMAL = "LeftRingProximal"
101    LEFT_RING_INTERMEDIATE = "LeftRingIntermediate"
102    LEFT_RING_DISTAL = "LeftRingDistal"
103    LEFT_LITTLE_PROXIMAL = "LeftLittleProximal"
104    LEFT_LITTLE_INTERMEDIATE = "LeftLittleIntermediate"
105    LEFT_LITTLE_DISTAL = "LeftLittleDistal"
106    RIGHT_THUMB_PROXIMAL = "RightThumbProximal"
107    RIGHT_THUMB_INTERMEDIATE = "RightThumbIntermediate"
108    RIGHT_THUMB_DISTAL = "RightThumbDistal"
109    RIGHT_INDEX_PROXIMAL = "RightIndexProximal"
110    RIGHT_INDEX_INTERMEDIATE = "RightIndexIntermediate"
111    RIGHT_INDEX_DISTAL = "RightIndexDistal"
112    RIGHT_MIDDLE_PROXIMAL = "RightMiddleProximal"
113    RIGHT_MIDDLE_INTERMEDIATE = "RightMiddleIntermediate"
114    RIGHT_MIDDLE_DISTAL = "RightMiddleDistal"
115    RIGHT_RING_PROXIMAL = "RightRingProximal"
116    RIGHT_RING_INTERMEDIATE = "RightRingIntermediate"
117    RIGHT_RING_DISTAL = "RightRingDistal"
118    RIGHT_LITTLE_PROXIMAL = "RightLittleProximal"
119    RIGHT_LITTLE_INTERMEDIATE = "RightLittleIntermediate"
120    RIGHT_LITTLE_DISTAL = "RightLittleDistal"

All 55 HumanBodyBones from Unity.

HIPS = <Bone.HIPS: 'Hips'>
LEFT_UPPER_LEG = <Bone.LEFT_UPPER_LEG: 'LeftUpperLeg'>
RIGHT_UPPER_LEG = <Bone.RIGHT_UPPER_LEG: 'RightUpperLeg'>
LEFT_LOWER_LEG = <Bone.LEFT_LOWER_LEG: 'LeftLowerLeg'>
RIGHT_LOWER_LEG = <Bone.RIGHT_LOWER_LEG: 'RightLowerLeg'>
LEFT_FOOT = <Bone.LEFT_FOOT: 'LeftFoot'>
RIGHT_FOOT = <Bone.RIGHT_FOOT: 'RightFoot'>
SPINE = <Bone.SPINE: 'Spine'>
CHEST = <Bone.CHEST: 'Chest'>
UPPER_CHEST = <Bone.UPPER_CHEST: 'UpperChest'>
NECK = <Bone.NECK: 'Neck'>
HEAD = <Bone.HEAD: 'Head'>
LEFT_SHOULDER = <Bone.LEFT_SHOULDER: 'LeftShoulder'>
RIGHT_SHOULDER = <Bone.RIGHT_SHOULDER: 'RightShoulder'>
LEFT_UPPER_ARM = <Bone.LEFT_UPPER_ARM: 'LeftUpperArm'>
RIGHT_UPPER_ARM = <Bone.RIGHT_UPPER_ARM: 'RightUpperArm'>
LEFT_LOWER_ARM = <Bone.LEFT_LOWER_ARM: 'LeftLowerArm'>
RIGHT_LOWER_ARM = <Bone.RIGHT_LOWER_ARM: 'RightLowerArm'>
LEFT_HAND = <Bone.LEFT_HAND: 'LeftHand'>
RIGHT_HAND = <Bone.RIGHT_HAND: 'RightHand'>
LEFT_TOES = <Bone.LEFT_TOES: 'LeftToes'>
RIGHT_TOES = <Bone.RIGHT_TOES: 'RightToes'>
LEFT_EYE = <Bone.LEFT_EYE: 'LeftEye'>
RIGHT_EYE = <Bone.RIGHT_EYE: 'RightEye'>
JAW = <Bone.JAW: 'Jaw'>
LEFT_THUMB_PROXIMAL = <Bone.LEFT_THUMB_PROXIMAL: 'LeftThumbProximal'>
LEFT_THUMB_INTERMEDIATE = <Bone.LEFT_THUMB_INTERMEDIATE: 'LeftThumbIntermediate'>
LEFT_THUMB_DISTAL = <Bone.LEFT_THUMB_DISTAL: 'LeftThumbDistal'>
LEFT_INDEX_PROXIMAL = <Bone.LEFT_INDEX_PROXIMAL: 'LeftIndexProximal'>
LEFT_INDEX_INTERMEDIATE = <Bone.LEFT_INDEX_INTERMEDIATE: 'LeftIndexIntermediate'>
LEFT_INDEX_DISTAL = <Bone.LEFT_INDEX_DISTAL: 'LeftIndexDistal'>
LEFT_MIDDLE_PROXIMAL = <Bone.LEFT_MIDDLE_PROXIMAL: 'LeftMiddleProximal'>
LEFT_MIDDLE_INTERMEDIATE = <Bone.LEFT_MIDDLE_INTERMEDIATE: 'LeftMiddleIntermediate'>
LEFT_MIDDLE_DISTAL = <Bone.LEFT_MIDDLE_DISTAL: 'LeftMiddleDistal'>
LEFT_RING_PROXIMAL = <Bone.LEFT_RING_PROXIMAL: 'LeftRingProximal'>
LEFT_RING_INTERMEDIATE = <Bone.LEFT_RING_INTERMEDIATE: 'LeftRingIntermediate'>
LEFT_RING_DISTAL = <Bone.LEFT_RING_DISTAL: 'LeftRingDistal'>
LEFT_LITTLE_PROXIMAL = <Bone.LEFT_LITTLE_PROXIMAL: 'LeftLittleProximal'>
LEFT_LITTLE_INTERMEDIATE = <Bone.LEFT_LITTLE_INTERMEDIATE: 'LeftLittleIntermediate'>
LEFT_LITTLE_DISTAL = <Bone.LEFT_LITTLE_DISTAL: 'LeftLittleDistal'>
RIGHT_THUMB_PROXIMAL = <Bone.RIGHT_THUMB_PROXIMAL: 'RightThumbProximal'>
RIGHT_THUMB_INTERMEDIATE = <Bone.RIGHT_THUMB_INTERMEDIATE: 'RightThumbIntermediate'>
RIGHT_THUMB_DISTAL = <Bone.RIGHT_THUMB_DISTAL: 'RightThumbDistal'>
RIGHT_INDEX_PROXIMAL = <Bone.RIGHT_INDEX_PROXIMAL: 'RightIndexProximal'>
RIGHT_INDEX_INTERMEDIATE = <Bone.RIGHT_INDEX_INTERMEDIATE: 'RightIndexIntermediate'>
RIGHT_INDEX_DISTAL = <Bone.RIGHT_INDEX_DISTAL: 'RightIndexDistal'>
RIGHT_MIDDLE_PROXIMAL = <Bone.RIGHT_MIDDLE_PROXIMAL: 'RightMiddleProximal'>
RIGHT_MIDDLE_INTERMEDIATE = <Bone.RIGHT_MIDDLE_INTERMEDIATE: 'RightMiddleIntermediate'>
RIGHT_MIDDLE_DISTAL = <Bone.RIGHT_MIDDLE_DISTAL: 'RightMiddleDistal'>
RIGHT_RING_PROXIMAL = <Bone.RIGHT_RING_PROXIMAL: 'RightRingProximal'>
RIGHT_RING_INTERMEDIATE = <Bone.RIGHT_RING_INTERMEDIATE: 'RightRingIntermediate'>
RIGHT_RING_DISTAL = <Bone.RIGHT_RING_DISTAL: 'RightRingDistal'>
RIGHT_LITTLE_PROXIMAL = <Bone.RIGHT_LITTLE_PROXIMAL: 'RightLittleProximal'>
RIGHT_LITTLE_INTERMEDIATE = <Bone.RIGHT_LITTLE_INTERMEDIATE: 'RightLittleIntermediate'>
RIGHT_LITTLE_DISTAL = <Bone.RIGHT_LITTLE_DISTAL: 'RightLittleDistal'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class BlendShapeKey(builtins.str, enum.Enum):
123class BlendShapeKey(str, Enum):
124    """BlendShapeKey interface."""

BlendShapeKey interface.

Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@dataclass(frozen=True, slots=True)
class CoordinateVector:
127@dataclass(frozen=True, slots=True)
128class CoordinateVector:
129    """Cartesian coordinate vector."""
130
131    x: float  # pylint: disable=invalid-name
132    """float: x coordinate."""
133
134    y: float  # pylint: disable=invalid-name
135    """float: y coordinate."""
136
137    z: float  # pylint: disable=invalid-name
138    """float: z coordinate."""
139
140    @classmethod
141    def identity(cls) -> 'CoordinateVector':
142        """Create an identity position.
143
144        Returns:
145            Position:
146                Created identity position (x, y, z).
147
148        >>> CoordinateVector.identity()
149        CoordinateVector(0.0, 0.0, 0.0)
150
151        """
152        return cls(0.0, 0.0, 0.0)

Cartesian coordinate vector.

CoordinateVector(x: float, y: float, z: float)
x: float

float: x coordinate.

y: float

float: y coordinate.

z: float

float: z coordinate.

@classmethod
def identity(cls) -> vmcp.typing.CoordinateVector:
140    @classmethod
141    def identity(cls) -> 'CoordinateVector':
142        """Create an identity position.
143
144        Returns:
145            Position:
146                Created identity position (x, y, z).
147
148        >>> CoordinateVector.identity()
149        CoordinateVector(0.0, 0.0, 0.0)
150
151        """
152        return cls(0.0, 0.0, 0.0)

Create an identity position.

Returns: Position: Created identity position (x, y, z).

>>> CoordinateVector.identity()
CoordinateVector(0.0, 0.0, 0.0)
@dataclass(frozen=True, slots=True)
class Quaternion:
155@dataclass(frozen=True, slots=True)
156class Quaternion:
157    """Quaternion."""
158
159    x: float  # pylint: disable=invalid-name
160    """float: x component."""
161
162    y: float  # pylint: disable=invalid-name
163    """float: y component."""
164
165    z: float  # pylint: disable=invalid-name
166    """float: z component."""
167
168    w: float  # pylint: disable=invalid-name
169    """float: w component."""
170
171    @classmethod
172    def identity(cls) -> 'Quaternion':
173        """Create an identity quaternion.
174
175        Returns:
176            Quaternion:
177                Created identity quaternion (x, y, z, w).
178
179        >>> Quaternion.identity()
180        0.0, 0.0, 0.0, 1.0
181
182        """
183        return cls(0.0, 0.0, 0.0, 1.0)
184
185    @classmethod
186    def from_rotvec(
187        cls,
188        x: float,  # pylint: disable=invalid-name
189        y: float,  # pylint: disable=invalid-name
190        z: float  # pylint: disable=invalid-name
191    ) -> 'Quaternion':
192        """Create from rotation vector.
193
194        Args:
195            x (float):
196                x dimension.
197            y (float):
198                y dimension.
199            z (float):
200                z dimension.
201
202        .. _Source:
203            https://github.com/scipy/scipy/blob/main/scipy/spatial/transform/_rotation.pyx#L872-L876
204
205        """
206        angle: float = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2))
207
208        # Stolen from the `scipy` package
209        if angle <= 1e-3:
210            # small angle
211            angle2 = angle * angle
212            scale = 0.5 - angle2 / 48 + angle2 * angle2 / 3840
213        else:
214            # large angle
215            scale = sin(angle / 2) / angle
216
217        return cls(
218            x * scale,
219            y * scale,
220            z * scale,
221            cos(angle / 2)
222        )
223
224    @classmethod
225    def from_euler(
226        cls,
227        phi: float,
228        theta: float,
229        psi: float
230    ) -> 'Quaternion':
231        """Create from euler angles.
232
233        Args:
234            phi (float):
235                Rotation angle around the X axis (radian).
236            theta (float):
237                Rotation angle around the Y axis (radian).
238            psi (float):
239                Rotation angle around the Y axis (radian).
240
241        Returns:
242            Quaternion:
243                Created quaternion (x, y, z, w).
244
245        .. _Source:
246            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
247
248        """
249        # x axis rotation angle
250        cos_phi_half = cos(phi / 2)
251        sin_phi_half = sin(phi / 2)
252        # y axis rotation angle
253        cos_theta_half = cos(theta / 2)
254        sin_theta_half = sin(theta / 2)
255        # z axis rotation angle
256        cos_psi_half = cos(psi / 2)
257        sin_psi_half = sin(psi / 2)
258        # Calculation
259        return cls(
260            x=float(
261                sin_phi_half * cos_theta_half * cos_psi_half -
262                cos_phi_half * sin_theta_half * sin_psi_half
263            ),
264            y=float(
265                cos_phi_half * sin_theta_half * cos_psi_half +
266                sin_phi_half * cos_theta_half * sin_psi_half
267            ),
268            z=float(
269                cos_phi_half * cos_theta_half * sin_psi_half -
270                sin_phi_half * sin_theta_half * cos_psi_half
271            ),
272            w=float(
273                cos_phi_half * cos_theta_half * cos_psi_half +
274                sin_phi_half * sin_theta_half * sin_psi_half
275            )
276        )
277
278    def to_euler(self) -> tuple[float, float, float]:
279        """Convert to euler angles.
280
281        Returns:
282            tuple[float, float, float]:
283                Phi, theta and psi (radian).
284
285        .. _Source:
286            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
287
288        """
289        # x axis rotation angle
290        term0 = 2 * (self.w * self.x + self.y * self.z)
291        term1 = 1 - 2 * (self.x * self.x + self.y * self.y)
292        phi = atan2(term0, term1)
293        # y axis rotation angle
294        term2 = 2 * (self.w * self.y - self.z * self.x)
295        term2 = 1 if term2 > 1 else term2
296        term2 = -1 if term2 < -1 else term2
297        theta = asin(term2)
298        # y axis rotation angle
299        term3 = 2 * (self.w * self.z + self.x * self.y)
300        term4 = 1 - 2 * (self.y * self.y + self.z * self.z)
301        psi = atan2(term3, term4)
302        return phi, theta, psi
303
304    def conjugate(self) -> 'Quaternion':
305        """Return conjugated quaternion.
306
307        Returns:
308            Quaternion:
309                Conjugated quaternion (x, y, z, w).
310
311        .. _Source:
312            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
313
314        """
315        # Calculation
316        return Quaternion(
317            x=-self.x,
318            y=-self.y,
319            z=-self.z,
320            w=self.w
321        )
322
323    def multiply_by(
324        self,
325        quaternion: 'Quaternion'
326    ) -> 'Quaternion':
327        """Multiplicate quaternion with an given one and return the product.
328
329        Returns:
330            Quaternion:
331                Multiplication product quaternion (x, y, z, w)
332
333        .. _Source:
334            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
335
336        """
337        # Calculation
338        return Quaternion(
339            x=float(
340                self.w * quaternion.x +
341                self.x * quaternion.w +
342                self.y * quaternion.z -
343                self.z * quaternion.y
344            ),
345            y=float(
346                self.w * quaternion.y +
347                self.y * quaternion.w +
348                self.z * quaternion.x -
349                self.x * quaternion.z
350            ),
351            z=float(
352                self.w * quaternion.z +
353                self.z * quaternion.w +
354                self.x * quaternion.y -
355                self.y * quaternion.x
356            ),
357            w=float(
358                self.w * quaternion.w -
359                self.x * quaternion.x -
360                self.y * quaternion.y -
361                self.z * quaternion.z
362            )
363        )

Quaternion.

Quaternion(x: float, y: float, z: float, w: float)
x: float

float: x component.

y: float

float: y component.

z: float

float: z component.

w: float

float: w component.

@classmethod
def identity(cls) -> vmcp.typing.Quaternion:
171    @classmethod
172    def identity(cls) -> 'Quaternion':
173        """Create an identity quaternion.
174
175        Returns:
176            Quaternion:
177                Created identity quaternion (x, y, z, w).
178
179        >>> Quaternion.identity()
180        0.0, 0.0, 0.0, 1.0
181
182        """
183        return cls(0.0, 0.0, 0.0, 1.0)

Create an identity quaternion.

Returns: Quaternion: Created identity quaternion (x, y, z, w).

>>> Quaternion.identity()
0.0, 0.0, 0.0, 1.0
@classmethod
def from_rotvec(cls, x: float, y: float, z: float) -> vmcp.typing.Quaternion:
185    @classmethod
186    def from_rotvec(
187        cls,
188        x: float,  # pylint: disable=invalid-name
189        y: float,  # pylint: disable=invalid-name
190        z: float  # pylint: disable=invalid-name
191    ) -> 'Quaternion':
192        """Create from rotation vector.
193
194        Args:
195            x (float):
196                x dimension.
197            y (float):
198                y dimension.
199            z (float):
200                z dimension.
201
202        .. _Source:
203            https://github.com/scipy/scipy/blob/main/scipy/spatial/transform/_rotation.pyx#L872-L876
204
205        """
206        angle: float = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2))
207
208        # Stolen from the `scipy` package
209        if angle <= 1e-3:
210            # small angle
211            angle2 = angle * angle
212            scale = 0.5 - angle2 / 48 + angle2 * angle2 / 3840
213        else:
214            # large angle
215            scale = sin(angle / 2) / angle
216
217        return cls(
218            x * scale,
219            y * scale,
220            z * scale,
221            cos(angle / 2)
222        )

Create from rotation vector.

Args: x (float): x dimension. y (float): y dimension. z (float): z dimension.

@classmethod
def from_euler(cls, phi: float, theta: float, psi: float) -> vmcp.typing.Quaternion:
224    @classmethod
225    def from_euler(
226        cls,
227        phi: float,
228        theta: float,
229        psi: float
230    ) -> 'Quaternion':
231        """Create from euler angles.
232
233        Args:
234            phi (float):
235                Rotation angle around the X axis (radian).
236            theta (float):
237                Rotation angle around the Y axis (radian).
238            psi (float):
239                Rotation angle around the Y axis (radian).
240
241        Returns:
242            Quaternion:
243                Created quaternion (x, y, z, w).
244
245        .. _Source:
246            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
247
248        """
249        # x axis rotation angle
250        cos_phi_half = cos(phi / 2)
251        sin_phi_half = sin(phi / 2)
252        # y axis rotation angle
253        cos_theta_half = cos(theta / 2)
254        sin_theta_half = sin(theta / 2)
255        # z axis rotation angle
256        cos_psi_half = cos(psi / 2)
257        sin_psi_half = sin(psi / 2)
258        # Calculation
259        return cls(
260            x=float(
261                sin_phi_half * cos_theta_half * cos_psi_half -
262                cos_phi_half * sin_theta_half * sin_psi_half
263            ),
264            y=float(
265                cos_phi_half * sin_theta_half * cos_psi_half +
266                sin_phi_half * cos_theta_half * sin_psi_half
267            ),
268            z=float(
269                cos_phi_half * cos_theta_half * sin_psi_half -
270                sin_phi_half * sin_theta_half * cos_psi_half
271            ),
272            w=float(
273                cos_phi_half * cos_theta_half * cos_psi_half +
274                sin_phi_half * sin_theta_half * sin_psi_half
275            )
276        )

Create from euler angles.

Args: phi (float): Rotation angle around the X axis (radian). theta (float): Rotation angle around the Y axis (radian). psi (float): Rotation angle around the Y axis (radian).

Returns: Quaternion: Created quaternion (x, y, z, w).

def to_euler(self) -> tuple[float, float, float]:
278    def to_euler(self) -> tuple[float, float, float]:
279        """Convert to euler angles.
280
281        Returns:
282            tuple[float, float, float]:
283                Phi, theta and psi (radian).
284
285        .. _Source:
286            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
287
288        """
289        # x axis rotation angle
290        term0 = 2 * (self.w * self.x + self.y * self.z)
291        term1 = 1 - 2 * (self.x * self.x + self.y * self.y)
292        phi = atan2(term0, term1)
293        # y axis rotation angle
294        term2 = 2 * (self.w * self.y - self.z * self.x)
295        term2 = 1 if term2 > 1 else term2
296        term2 = -1 if term2 < -1 else term2
297        theta = asin(term2)
298        # y axis rotation angle
299        term3 = 2 * (self.w * self.z + self.x * self.y)
300        term4 = 1 - 2 * (self.y * self.y + self.z * self.z)
301        psi = atan2(term3, term4)
302        return phi, theta, psi

Convert to euler angles.

Returns: tuple[float, float, float]: Phi, theta and psi (radian).

def conjugate(self) -> vmcp.typing.Quaternion:
304    def conjugate(self) -> 'Quaternion':
305        """Return conjugated quaternion.
306
307        Returns:
308            Quaternion:
309                Conjugated quaternion (x, y, z, w).
310
311        .. _Source:
312            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
313
314        """
315        # Calculation
316        return Quaternion(
317            x=-self.x,
318            y=-self.y,
319            z=-self.z,
320            w=self.w
321        )

Return conjugated quaternion.

Returns: Quaternion: Conjugated quaternion (x, y, z, w).

def multiply_by(self, quaternion: vmcp.typing.Quaternion) -> vmcp.typing.Quaternion:
323    def multiply_by(
324        self,
325        quaternion: 'Quaternion'
326    ) -> 'Quaternion':
327        """Multiplicate quaternion with an given one and return the product.
328
329        Returns:
330            Quaternion:
331                Multiplication product quaternion (x, y, z, w)
332
333        .. _Source:
334            https://www.meccanismocomplesso.org/en/hamiltons-quaternions-and-3d-rotation-with-python
335
336        """
337        # Calculation
338        return Quaternion(
339            x=float(
340                self.w * quaternion.x +
341                self.x * quaternion.w +
342                self.y * quaternion.z -
343                self.z * quaternion.y
344            ),
345            y=float(
346                self.w * quaternion.y +
347                self.y * quaternion.w +
348                self.z * quaternion.x -
349                self.x * quaternion.z
350            ),
351            z=float(
352                self.w * quaternion.z +
353                self.z * quaternion.w +
354                self.x * quaternion.y -
355                self.y * quaternion.x
356            ),
357            w=float(
358                self.w * quaternion.w -
359                self.x * quaternion.x -
360                self.y * quaternion.y -
361                self.z * quaternion.z
362            )
363        )

Multiplicate quaternion with an given one and return the product.

Returns: Quaternion: Multiplication product quaternion (x, y, z, w)

@dataclass(frozen=False, slots=True, init=False)
class Scale:
366@dataclass(frozen=False, slots=True, init=False)
367class Scale:
368    """Scale."""
369
370    width: float
371    """float: Width dimension."""
372
373    height: float
374    """float: Height dimension."""
375
376    length: float
377    """float: Length dimension."""
378
379    @overload
380    def __init__(
381        self,
382        uniformly: float
383    ) -> None:
384        """Scale constructor.
385
386        Args:
387            uniformly (float):
388                All dimensions.
389
390        """
391
392    @overload
393    def __init__(
394        self,
395        width: float,
396        height: float,
397        length: float
398    ) -> None:
399        """Scale constructor.
400
401        Args:
402            width (float):
403                Width dimension.
404            height (float):
405                Height dimension.
406            length (float):
407                Length dimension.
408
409        """
410
411    def __init__(
412        self,
413        *args: float,
414        **kwargs: float
415    ) -> None:
416        """Implement scale constructor.
417
418        Args:
419            *args (float):
420                1 OR 3 dimension arguments.
421        Raises:
422            ValueError:
423                If invalid arguments are given.
424
425        """
426        args += tuple(kwargs.values())
427        match len(args):
428            case 1:
429                self.width = self.height = self.length = args[0]
430            case 3:
431                self.width = args[0]
432                self.height = args[1]
433                self.length = args[2]
434            case _:
435                raise ValueError(f"Invalid parameters given: {str(args)}")

Scale.

Scale(*args: float, **kwargs: float)
411    def __init__(
412        self,
413        *args: float,
414        **kwargs: float
415    ) -> None:
416        """Implement scale constructor.
417
418        Args:
419            *args (float):
420                1 OR 3 dimension arguments.
421        Raises:
422            ValueError:
423                If invalid arguments are given.
424
425        """
426        args += tuple(kwargs.values())
427        match len(args):
428            case 1:
429                self.width = self.height = self.length = args[0]
430            case 3:
431                self.width = args[0]
432                self.height = args[1]
433                self.length = args[2]
434            case _:
435                raise ValueError(f"Invalid parameters given: {str(args)}")

Implement scale constructor.

Args: *args (float): 1 OR 3 dimension arguments. Raises: ValueError: If invalid arguments are given.

width: float

float: Width dimension.

height: float

float: Height dimension.

length: float

float: Length dimension.

class Timestamp(builtins.float):
439class Timestamp(float):
440    """Timestamp since the epoch in seconds with it's fractions."""
441
442    def __new__(
443        cls,
444        at: float = None
445    ) -> 'Timestamp':
446        """Create a new instance of class ``Timestamp``.
447
448        Args:
449            at (Optional[float]):
450                Seconds with it's fractions since the epoch.
451
452        Returns:
453            Timestamp:
454                Object instance.
455
456        """
457        return super().__new__(cls, time() if at is None else at)
458
459    def elapsed(self) -> float:
460        """Return elapsed time in seconds with it's fractions as float.
461
462        Returns:
463            float:
464                Elapsed time in seconds with it's fractions.
465
466        >>> Timestamp().elapsed()
467        0.0
468
469        """
470        return time() - self
471
472    def __str__(self) -> str:
473        """Return string representation of the object.
474
475        Returns:
476            str:
477                Representation of the object.
478
479        """
480        return super().__repr__()
481
482    def __repr__(self) -> str:
483        """Return a string, representing the object in a reconstructable way.
484
485        Returns:
486            str:
487                Representing the object in a reconstructable way.
488
489        """
490        return f"{self.__class__.__qualname__}(at={self})"

Timestamp since the epoch in seconds with it's fractions.

Timestamp(at: float = None)
442    def __new__(
443        cls,
444        at: float = None
445    ) -> 'Timestamp':
446        """Create a new instance of class ``Timestamp``.
447
448        Args:
449            at (Optional[float]):
450                Seconds with it's fractions since the epoch.
451
452        Returns:
453            Timestamp:
454                Object instance.
455
456        """
457        return super().__new__(cls, time() if at is None else at)

Create a new instance of class Timestamp.

Args: at (Optional[float]): Seconds with it's fractions since the epoch.

Returns: Timestamp: Object instance.

def elapsed(self) -> float:
459    def elapsed(self) -> float:
460        """Return elapsed time in seconds with it's fractions as float.
461
462        Returns:
463            float:
464                Elapsed time in seconds with it's fractions.
465
466        >>> Timestamp().elapsed()
467        0.0
468
469        """
470        return time() - self

Return elapsed time in seconds with it's fractions as float.

Returns: float: Elapsed time in seconds with it's fractions.

>>> Timestamp().elapsed()
0.0
Inherited Members
builtins.float
conjugate
as_integer_ratio
fromhex
hex
is_integer
real
imag