antioch-py 2.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of antioch-py might be problematic. Click here for more details.

Files changed (99) hide show
  1. antioch/__init__.py +0 -0
  2. antioch/message.py +87 -0
  3. antioch/module/__init__.py +53 -0
  4. antioch/module/clock.py +62 -0
  5. antioch/module/execution.py +278 -0
  6. antioch/module/input.py +127 -0
  7. antioch/module/module.py +218 -0
  8. antioch/module/node.py +357 -0
  9. antioch/module/token.py +42 -0
  10. antioch/session/__init__.py +150 -0
  11. antioch/session/ark.py +504 -0
  12. antioch/session/asset.py +65 -0
  13. antioch/session/error.py +80 -0
  14. antioch/session/record.py +158 -0
  15. antioch/session/scene.py +1521 -0
  16. antioch/session/session.py +220 -0
  17. antioch/session/task.py +323 -0
  18. antioch/session/views/__init__.py +40 -0
  19. antioch/session/views/animation.py +189 -0
  20. antioch/session/views/articulation.py +245 -0
  21. antioch/session/views/basis_curve.py +186 -0
  22. antioch/session/views/camera.py +92 -0
  23. antioch/session/views/collision.py +75 -0
  24. antioch/session/views/geometry.py +74 -0
  25. antioch/session/views/ground_plane.py +63 -0
  26. antioch/session/views/imu.py +73 -0
  27. antioch/session/views/joint.py +64 -0
  28. antioch/session/views/light.py +175 -0
  29. antioch/session/views/pir_sensor.py +140 -0
  30. antioch/session/views/radar.py +73 -0
  31. antioch/session/views/rigid_body.py +282 -0
  32. antioch/session/views/xform.py +119 -0
  33. antioch_py-2.0.6.dist-info/METADATA +115 -0
  34. antioch_py-2.0.6.dist-info/RECORD +99 -0
  35. antioch_py-2.0.6.dist-info/WHEEL +5 -0
  36. antioch_py-2.0.6.dist-info/entry_points.txt +2 -0
  37. antioch_py-2.0.6.dist-info/top_level.txt +2 -0
  38. common/__init__.py +0 -0
  39. common/ark/__init__.py +60 -0
  40. common/ark/ark.py +128 -0
  41. common/ark/hardware.py +121 -0
  42. common/ark/kinematics.py +31 -0
  43. common/ark/module.py +85 -0
  44. common/ark/node.py +94 -0
  45. common/ark/scheduler.py +439 -0
  46. common/ark/sim.py +33 -0
  47. common/assets/__init__.py +3 -0
  48. common/constants.py +47 -0
  49. common/core/__init__.py +52 -0
  50. common/core/agent.py +296 -0
  51. common/core/auth.py +305 -0
  52. common/core/registry.py +331 -0
  53. common/core/task.py +36 -0
  54. common/message/__init__.py +59 -0
  55. common/message/annotation.py +89 -0
  56. common/message/array.py +500 -0
  57. common/message/base.py +517 -0
  58. common/message/camera.py +91 -0
  59. common/message/color.py +139 -0
  60. common/message/frame.py +50 -0
  61. common/message/image.py +171 -0
  62. common/message/imu.py +14 -0
  63. common/message/joint.py +47 -0
  64. common/message/log.py +31 -0
  65. common/message/pir.py +16 -0
  66. common/message/point.py +109 -0
  67. common/message/point_cloud.py +63 -0
  68. common/message/pose.py +148 -0
  69. common/message/quaternion.py +273 -0
  70. common/message/radar.py +58 -0
  71. common/message/types.py +37 -0
  72. common/message/vector.py +786 -0
  73. common/rome/__init__.py +9 -0
  74. common/rome/client.py +430 -0
  75. common/rome/error.py +16 -0
  76. common/session/__init__.py +54 -0
  77. common/session/environment.py +31 -0
  78. common/session/sim.py +240 -0
  79. common/session/views/__init__.py +263 -0
  80. common/session/views/animation.py +73 -0
  81. common/session/views/articulation.py +184 -0
  82. common/session/views/basis_curve.py +102 -0
  83. common/session/views/camera.py +147 -0
  84. common/session/views/collision.py +59 -0
  85. common/session/views/geometry.py +102 -0
  86. common/session/views/ground_plane.py +41 -0
  87. common/session/views/imu.py +66 -0
  88. common/session/views/joint.py +81 -0
  89. common/session/views/light.py +96 -0
  90. common/session/views/pir_sensor.py +115 -0
  91. common/session/views/radar.py +82 -0
  92. common/session/views/rigid_body.py +236 -0
  93. common/session/views/viewport.py +21 -0
  94. common/session/views/xform.py +39 -0
  95. common/utils/__init__.py +4 -0
  96. common/utils/comms.py +571 -0
  97. common/utils/logger.py +123 -0
  98. common/utils/time.py +42 -0
  99. common/utils/usd.py +12 -0
@@ -0,0 +1,786 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ import numpy as np
6
+ from pydantic import model_validator
7
+
8
+ from common.message.base import Message
9
+ from common.message.point import Point2, Point3
10
+
11
+ if TYPE_CHECKING:
12
+ from common.message.quaternion import Quaternion
13
+
14
+ try:
15
+ from pxr import Gf # type: ignore
16
+
17
+ HAS_PXR = True
18
+ except ImportError:
19
+ HAS_PXR = False
20
+
21
+
22
+ class Vector2(Message):
23
+ """
24
+ A 2D vector (x, y).
25
+
26
+ Serializes as a tuple [x, y] for space efficiency.
27
+ Automatically converts from common Python types:
28
+ - Lists: Vector2.from_any([1.0, 2.0])
29
+ - Tuples: Vector2.from_any((1.0, 2.0))
30
+ - Dicts: Vector2.from_any({"x": 1.0, "y": 2.0})
31
+ - Numpy arrays: Vector2.from_any(np.array([1.0, 2.0]))
32
+
33
+ When used as a field in Messages, conversion happens automatically:
34
+ pose = SomeMessage(position_2d=[10.0, 20.0]) # List auto-converts!
35
+ """
36
+
37
+ _type = "antioch/vector2"
38
+ data: tuple[float, float]
39
+
40
+ @property
41
+ def x(self) -> float:
42
+ """Get the x component.
43
+
44
+ :return: The x component.
45
+ """
46
+
47
+ return self.data[0]
48
+
49
+ @property
50
+ def y(self) -> float:
51
+ """Get the y component.
52
+
53
+ :return: The y component.
54
+ """
55
+
56
+ return self.data[1]
57
+
58
+ def __len__(self) -> int:
59
+ """
60
+ Get the length of the vector (always 2).
61
+
62
+ :return: The length of the vector.
63
+ """
64
+
65
+ return 2
66
+
67
+ def __iter__(self):
68
+ """
69
+ Iterate over vector components.
70
+
71
+ :return: Iterator over [x, y].
72
+ """
73
+
74
+ return iter(self.data)
75
+
76
+ def __getitem__(self, index: int) -> float:
77
+ """
78
+ Get vector component by index.
79
+
80
+ :param index: Index (0=x, 1=y).
81
+ :return: The component value.
82
+ """
83
+
84
+ return self.data[index]
85
+
86
+ def __eq__(self, other: object) -> bool:
87
+ """
88
+ Check equality with another Vector2.
89
+
90
+ :param other: Another Vector2.
91
+ :return: True if equal.
92
+ """
93
+
94
+ if not isinstance(other, Vector2):
95
+ return False
96
+ return self.data == other.data
97
+
98
+ def __repr__(self) -> str:
99
+ """
100
+ Return a readable string representation.
101
+
102
+ :return: String representation.
103
+ """
104
+
105
+ return f"Vector2(x={self.x}, y={self.y})"
106
+
107
+ def __str__(self) -> str:
108
+ """
109
+ Return a readable string representation.
110
+
111
+ :return: String representation.
112
+ """
113
+
114
+ return f"Vector2(x={self.x}, y={self.y})"
115
+
116
+ def __add__(self, other: Vector2) -> Vector2:
117
+ """
118
+ Add two vectors component-wise.
119
+
120
+ :param other: Another Vector2.
121
+ :return: The sum vector.
122
+ """
123
+
124
+ return Vector2(data=(self.x + other.x, self.y + other.y))
125
+
126
+ def __sub__(self, other: Vector2) -> Vector2:
127
+ """
128
+ Subtract two vectors component-wise.
129
+
130
+ :param other: Another Vector2.
131
+ :return: The difference vector.
132
+ """
133
+
134
+ return Vector2(data=(self.x - other.x, self.y - other.y))
135
+
136
+ def __mul__(self, scalar: float) -> Vector2:
137
+ """
138
+ Multiply vector by a scalar.
139
+
140
+ :param scalar: The scalar value.
141
+ :return: The scaled vector.
142
+ """
143
+
144
+ return Vector2(data=(self.x * scalar, self.y * scalar))
145
+
146
+ def __rmul__(self, scalar: float) -> Vector2:
147
+ """
148
+ Multiply vector by a scalar (reversed operands).
149
+
150
+ :param scalar: The scalar value.
151
+ :return: The scaled vector.
152
+ """
153
+
154
+ return self.__mul__(scalar)
155
+
156
+ def __truediv__(self, scalar: float) -> Vector2:
157
+ """
158
+ Divide vector by a scalar.
159
+
160
+ :param scalar: The scalar value.
161
+ :return: The scaled vector.
162
+ :raises ZeroDivisionError: If scalar is zero.
163
+ """
164
+
165
+ if scalar == 0:
166
+ raise ZeroDivisionError("Cannot divide vector by zero")
167
+ return Vector2(data=(self.x / scalar, self.y / scalar))
168
+
169
+ def __neg__(self) -> Vector2:
170
+ """
171
+ Negate the vector.
172
+
173
+ :return: The negated vector.
174
+ """
175
+
176
+ return Vector2(data=(-self.x, -self.y))
177
+
178
+ @model_validator(mode="before")
179
+ @classmethod
180
+ def convert_iterables(cls, data: Any) -> Any:
181
+ """
182
+ Automatically convert lists, tuples, dicts, and numpy arrays to Vector2 format.
183
+ """
184
+
185
+ # Already a Vector2 instance
186
+ if isinstance(data, cls):
187
+ return {"data": data.data}
188
+
189
+ # Tuple format - already correct
190
+ if isinstance(data, tuple) and len(data) == 2:
191
+ return {"data": (float(data[0]), float(data[1]))}
192
+
193
+ # Dict format with x, y - convert to tuple
194
+ if isinstance(data, dict):
195
+ if "data" in data:
196
+ return data
197
+ if "x" in data and "y" in data:
198
+ return {"data": (float(data["x"]), float(data["y"]))}
199
+ raise ValueError("Dict must have either 'data' or 'x' and 'y' fields")
200
+
201
+ # Numpy array - convert
202
+ if isinstance(data, np.ndarray):
203
+ if data.shape != (2,):
204
+ raise ValueError(f"Vector2 array must have shape (2,), got {data.shape}")
205
+ return {"data": (float(data[0]), float(data[1]))}
206
+
207
+ # List - convert
208
+ if isinstance(data, list):
209
+ if len(data) != 2:
210
+ raise ValueError(f"Vector2 requires 2 values, got {len(data)}")
211
+ return {"data": (float(data[0]), float(data[1]))}
212
+
213
+ return data
214
+
215
+ @classmethod
216
+ def new(cls, x: float, y: float) -> Vector2:
217
+ """
218
+ Create a new 2D vector.
219
+
220
+ :param x: The x component.
221
+ :param y: The y component.
222
+ :return: A 2D vector.
223
+ """
224
+
225
+ return cls(data=(x, y))
226
+
227
+ @classmethod
228
+ def zeros(cls) -> Vector2:
229
+ """
230
+ Create a zero vector.
231
+
232
+ :return: A zero vector.
233
+ """
234
+
235
+ return cls(data=(0.0, 0.0))
236
+
237
+ @classmethod
238
+ def ones(cls) -> Vector2:
239
+ """
240
+ Create a ones vector.
241
+
242
+ :return: A ones vector.
243
+ """
244
+
245
+ return cls(data=(1.0, 1.0))
246
+
247
+ @classmethod
248
+ def from_any(cls, data: Any) -> Vector2:
249
+ """
250
+ Create Vector2 from any iterable (list, tuple, numpy array).
251
+
252
+ :param data: Iterable with 2 values, dict, or a Vector2 instance.
253
+ :return: A Vector2 instance.
254
+ :raises ValueError: If conversion fails.
255
+ """
256
+
257
+ # Already a Vector2 - return as-is
258
+ if isinstance(data, cls):
259
+ return data
260
+
261
+ try:
262
+ # Will be handled by validator
263
+ return cls.model_validate(data)
264
+ except Exception as e:
265
+ raise ValueError(f"Cannot convert to Vector2: {e}") from None
266
+
267
+ @classmethod
268
+ def from_numpy(cls, array: np.ndarray) -> Vector2:
269
+ """
270
+ Create from a numpy array.
271
+
272
+ :param array: The numpy array (must have shape (2,)).
273
+ :return: A Vector2.
274
+ :raises ValueError: If array shape is not (2,).
275
+ """
276
+
277
+ if array.shape != (2,):
278
+ raise ValueError(f"Vector2 array must have shape (2,), got {array.shape}")
279
+ return cls(data=(float(array[0]), float(array[1])))
280
+
281
+ @classmethod
282
+ def from_list(cls, values: list[float]) -> Vector2:
283
+ """
284
+ Create from a list of 2 values.
285
+
286
+ :param values: List of 2 float values.
287
+ :return: A Vector2.
288
+ :raises ValueError: If list does not have exactly 2 values.
289
+ """
290
+
291
+ if len(values) != 2:
292
+ raise ValueError(f"Vector2 requires 2 values, got {len(values)}")
293
+ return cls(data=(values[0], values[1]))
294
+
295
+ def dot(self, other: Vector2) -> float:
296
+ """
297
+ Compute dot product with another vector.
298
+
299
+ :param other: Another Vector2.
300
+ :return: The dot product.
301
+ """
302
+
303
+ return self.x * other.x + self.y * other.y
304
+
305
+ def magnitude(self) -> float:
306
+ """
307
+ Compute the magnitude (length) of the vector.
308
+
309
+ :return: The magnitude.
310
+ """
311
+
312
+ return (self.x**2 + self.y**2) ** 0.5
313
+
314
+ def magnitude_squared(self) -> float:
315
+ """
316
+ Compute the squared magnitude of the vector.
317
+
318
+ :return: The squared magnitude.
319
+ """
320
+
321
+ return self.x**2 + self.y**2
322
+
323
+ def normalize(self) -> Vector2:
324
+ """
325
+ Return a normalized (unit length) version of this vector.
326
+
327
+ :return: The normalized vector.
328
+ :raises ValueError: If the vector has zero magnitude.
329
+ """
330
+
331
+ mag = self.magnitude()
332
+ if mag == 0:
333
+ raise ValueError("Cannot normalize zero vector")
334
+ return self / mag
335
+
336
+ def to_numpy(self) -> np.ndarray:
337
+ """
338
+ Convert to a numpy array.
339
+
340
+ :return: The numpy array.
341
+ """
342
+
343
+ return np.array(self.data, dtype=np.float32)
344
+
345
+ def to_list(self) -> list[float]:
346
+ """
347
+ Convert to a list.
348
+
349
+ :return: The list of values.
350
+ """
351
+
352
+ return list(self.data)
353
+
354
+ def to_point(self) -> Point2:
355
+ """
356
+ Convert to a Point2.
357
+
358
+ :return: A Point2 with the same x, y coordinates.
359
+ """
360
+
361
+ return Point2(x=self.x, y=self.y)
362
+
363
+ def as_array(self) -> list[float]:
364
+ """
365
+ Convert to a list (alias for to_list for Rust compatibility).
366
+
367
+ :return: The list of values.
368
+ """
369
+
370
+ return self.to_list()
371
+
372
+
373
+ class Vector3(Message):
374
+ """
375
+ A 3D vector (x, y, z).
376
+
377
+ Serializes as a tuple [x, y, z] for space efficiency.
378
+ Automatically converts from common Python types:
379
+ - Lists: Vector3.from_any([1.0, 2.0, 3.0])
380
+ - Tuples: Vector3.from_any((1.0, 2.0, 3.0))
381
+ - Dicts: Vector3.from_any({"x": 1.0, "y": 2.0, "z": 3.0})
382
+ - Numpy arrays: Vector3.from_any(np.array([1.0, 2.0, 3.0]))
383
+
384
+ When used as a field in Messages, conversion happens automatically:
385
+ pose = SomeMessage(position=[10.0, 20.0, 30.0]) # List auto-converts!
386
+ """
387
+
388
+ _type = "antioch/vector3"
389
+ data: tuple[float, float, float]
390
+
391
+ @property
392
+ def x(self) -> float:
393
+ """Get the x component.
394
+
395
+ :return: The x component.
396
+ """
397
+
398
+ return self.data[0]
399
+
400
+ @property
401
+ def y(self) -> float:
402
+ """Get the y component.
403
+
404
+ :return: The y component.
405
+ """
406
+
407
+ return self.data[1]
408
+
409
+ @property
410
+ def z(self) -> float:
411
+ """Get the z component.
412
+
413
+ :return: The z component.
414
+ """
415
+
416
+ return self.data[2]
417
+
418
+ def __len__(self) -> int:
419
+ """
420
+ Get the length of the vector (always 3).
421
+
422
+ :return: The length of the vector.
423
+ """
424
+
425
+ return 3
426
+
427
+ def __iter__(self):
428
+ """
429
+ Iterate over vector components.
430
+
431
+ :return: Iterator over [x, y, z].
432
+ """
433
+
434
+ return iter(self.data)
435
+
436
+ def __getitem__(self, index: int) -> float:
437
+ """
438
+ Get vector component by index.
439
+
440
+ :param index: Index (0=x, 1=y, 2=z).
441
+ :return: The component value.
442
+ """
443
+
444
+ return self.data[index]
445
+
446
+ def __eq__(self, other: object) -> bool:
447
+ """
448
+ Check equality with another Vector3.
449
+
450
+ :param other: Another Vector3.
451
+ :return: True if equal.
452
+ """
453
+
454
+ if not isinstance(other, Vector3):
455
+ return False
456
+ return self.data == other.data
457
+
458
+ def __repr__(self) -> str:
459
+ """
460
+ Return a readable string representation.
461
+
462
+ :return: String representation.
463
+ """
464
+
465
+ return f"Vector3(x={self.x}, y={self.y}, z={self.z})"
466
+
467
+ def __str__(self) -> str:
468
+ """
469
+ Return a readable string representation.
470
+
471
+ :return: String representation.
472
+ """
473
+
474
+ return f"Vector3(x={self.x}, y={self.y}, z={self.z})"
475
+
476
+ def __add__(self, other: Vector3) -> Vector3:
477
+ """
478
+ Add two vectors component-wise.
479
+
480
+ :param other: Another Vector3.
481
+ :return: The sum vector.
482
+ """
483
+
484
+ return Vector3(data=(self.x + other.x, self.y + other.y, self.z + other.z))
485
+
486
+ def __sub__(self, other: Vector3) -> Vector3:
487
+ """
488
+ Subtract two vectors component-wise.
489
+
490
+ :param other: Another Vector3.
491
+ :return: The difference vector.
492
+ """
493
+
494
+ return Vector3(data=(self.x - other.x, self.y - other.y, self.z - other.z))
495
+
496
+ def __mul__(self, scalar: float) -> Vector3:
497
+ """
498
+ Multiply vector by a scalar.
499
+
500
+ :param scalar: The scalar value.
501
+ :return: The scaled vector.
502
+ """
503
+
504
+ return Vector3(data=(self.x * scalar, self.y * scalar, self.z * scalar))
505
+
506
+ def __rmul__(self, scalar: float) -> Vector3:
507
+ """
508
+ Multiply vector by a scalar (reversed operands).
509
+
510
+ :param scalar: The scalar value.
511
+ :return: The scaled vector.
512
+ """
513
+
514
+ return self.__mul__(scalar)
515
+
516
+ def __truediv__(self, scalar: float) -> Vector3:
517
+ """
518
+ Divide vector by a scalar.
519
+
520
+ :param scalar: The scalar value.
521
+ :return: The scaled vector.
522
+ :raises ZeroDivisionError: If scalar is zero.
523
+ """
524
+
525
+ if scalar == 0:
526
+ raise ZeroDivisionError("Cannot divide vector by zero")
527
+ return Vector3(data=(self.x / scalar, self.y / scalar, self.z / scalar))
528
+
529
+ def __neg__(self) -> Vector3:
530
+ """
531
+ Negate the vector.
532
+
533
+ :return: The negated vector.
534
+ """
535
+
536
+ return Vector3(data=(-self.x, -self.y, -self.z))
537
+
538
+ @model_validator(mode="before")
539
+ @classmethod
540
+ def convert_iterables(cls, data: Any) -> Any:
541
+ """
542
+ Automatically convert lists, tuples, dicts, and numpy arrays to Vector3 format.
543
+ """
544
+
545
+ # Already a Vector3 instance
546
+ if isinstance(data, cls):
547
+ return {"data": data.data}
548
+
549
+ # Tuple format - already correct
550
+ if isinstance(data, tuple) and len(data) == 3:
551
+ return {"data": (float(data[0]), float(data[1]), float(data[2]))}
552
+
553
+ # Dict format with x, y, z - convert to tuple
554
+ if isinstance(data, dict):
555
+ if "data" in data:
556
+ return data
557
+ if "x" in data and "y" in data and "z" in data:
558
+ return {"data": (float(data["x"]), float(data["y"]), float(data["z"]))}
559
+ raise ValueError("Dict must have either 'data' or 'x', 'y', and 'z' fields")
560
+
561
+ # Numpy array - convert
562
+ if isinstance(data, np.ndarray):
563
+ if data.shape != (3,):
564
+ raise ValueError(f"Vector3 array must have shape (3,), got {data.shape}")
565
+ return {"data": (float(data[0]), float(data[1]), float(data[2]))}
566
+
567
+ # List - convert
568
+ if isinstance(data, list):
569
+ if len(data) != 3:
570
+ raise ValueError(f"Vector3 requires 3 values, got {len(data)}")
571
+ if all(item is None for item in data):
572
+ return {"data": (0.0, 0.0, 0.0)}
573
+ return {"data": (float(data[0]), float(data[1]), float(data[2]))}
574
+
575
+ return data
576
+
577
+ @classmethod
578
+ def new(cls, x: float, y: float, z: float) -> Vector3:
579
+ """
580
+ Create a new 3D vector.
581
+
582
+ :param x: The x component.
583
+ :param y: The y component.
584
+ :param z: The z component.
585
+ :return: A 3D vector.
586
+ """
587
+
588
+ return cls(data=(x, y, z))
589
+
590
+ @classmethod
591
+ def zeros(cls) -> Vector3:
592
+ """
593
+ Create a zero vector.
594
+
595
+ :return: A zero vector.
596
+ """
597
+
598
+ return cls(data=(0.0, 0.0, 0.0))
599
+
600
+ @classmethod
601
+ def ones(cls) -> Vector3:
602
+ """
603
+ Create a ones vector.
604
+
605
+ :return: A ones vector.
606
+ """
607
+
608
+ return cls(data=(1.0, 1.0, 1.0))
609
+
610
+ @classmethod
611
+ def from_any(cls, data: Any) -> Vector3:
612
+ """
613
+ Create Vector3 from any iterable (list, tuple, numpy array).
614
+
615
+ :param data: Iterable with 3 values, dict, or a Vector3 instance.
616
+ :return: A Vector3 instance.
617
+ :raises ValueError: If conversion fails.
618
+ """
619
+
620
+ # Already a Vector3 - return as-is
621
+ if isinstance(data, cls):
622
+ return data
623
+
624
+ try:
625
+ # Will be handled by validator
626
+ return cls.model_validate(data)
627
+ except Exception as e:
628
+ raise ValueError(f"Cannot convert to Vector3: {e}") from None
629
+
630
+ @classmethod
631
+ def from_numpy(cls, array: np.ndarray) -> Vector3:
632
+ """
633
+ Create from a numpy array.
634
+
635
+ :param array: The numpy array (must have shape (3,)).
636
+ :return: A Vector3.
637
+ :raises ValueError: If array shape is not (3,).
638
+ """
639
+
640
+ if array.shape != (3,):
641
+ raise ValueError(f"Vector3 array must have shape (3,), got {array.shape}")
642
+ return cls(data=(float(array[0]), float(array[1]), float(array[2])))
643
+
644
+ @classmethod
645
+ def from_list(cls, values: list[float]) -> Vector3:
646
+ """
647
+ Create from a list of 3 values.
648
+
649
+ :param values: List of 3 float values.
650
+ :return: A Vector3.
651
+ :raises ValueError: If list does not have exactly 3 values.
652
+ """
653
+
654
+ if len(values) != 3:
655
+ raise ValueError(f"Vector3 requires 3 values, got {len(values)}")
656
+ return cls(data=(values[0], values[1], values[2]))
657
+
658
+ def dot(self, other: Vector3) -> float:
659
+ """
660
+ Compute dot product with another vector.
661
+
662
+ :param other: Another Vector3.
663
+ :return: The dot product.
664
+ """
665
+
666
+ return self.x * other.x + self.y * other.y + self.z * other.z
667
+
668
+ def cross(self, other: Vector3) -> Vector3:
669
+ """
670
+ Compute cross product with another vector.
671
+
672
+ :param other: Another Vector3.
673
+ :return: The cross product vector.
674
+ """
675
+
676
+ return Vector3(
677
+ data=(
678
+ self.y * other.z - self.z * other.y,
679
+ self.z * other.x - self.x * other.z,
680
+ self.x * other.y - self.y * other.x,
681
+ )
682
+ )
683
+
684
+ def magnitude(self) -> float:
685
+ """
686
+ Compute the magnitude (length) of the vector.
687
+
688
+ :return: The magnitude.
689
+ """
690
+
691
+ return (self.x**2 + self.y**2 + self.z**2) ** 0.5
692
+
693
+ def magnitude_squared(self) -> float:
694
+ """
695
+ Compute the squared magnitude of the vector.
696
+
697
+ :return: The squared magnitude.
698
+ """
699
+
700
+ return self.x**2 + self.y**2 + self.z**2
701
+
702
+ def normalize(self) -> Vector3:
703
+ """
704
+ Return a normalized (unit length) version of this vector.
705
+
706
+ :return: The normalized vector.
707
+ :raises ValueError: If the vector has zero magnitude.
708
+ """
709
+
710
+ mag = self.magnitude()
711
+ if mag == 0:
712
+ raise ValueError("Cannot normalize zero vector")
713
+ return self / mag
714
+
715
+ def to_numpy(self) -> np.ndarray:
716
+ """
717
+ Convert to a numpy array.
718
+
719
+ :return: The numpy array.
720
+ """
721
+
722
+ return np.array(self.data, dtype=np.float32)
723
+
724
+ def to_list(self) -> list[float]:
725
+ """
726
+ Convert to a list.
727
+
728
+ :return: The list of values.
729
+ """
730
+
731
+ return list(self.data)
732
+
733
+ def to_gf_vec3f(self) -> Gf.Vec3f:
734
+ """
735
+ Convert to a Gf.Vec3f.
736
+
737
+ :return: The Gf.Vec3f.
738
+ :raises ImportError: If pxr is not installed.
739
+ """
740
+
741
+ if not HAS_PXR:
742
+ raise ImportError("pxr is not installed")
743
+ return Gf.Vec3f(self.x, self.y, self.z)
744
+
745
+ def to_gf_vec3d(self) -> Gf.Vec3d:
746
+ """
747
+ Convert to a Gf.Vec3d (double precision).
748
+
749
+ :return: The Gf.Vec3d.
750
+ :raises ImportError: If pxr is not installed.
751
+ """
752
+
753
+ if not HAS_PXR:
754
+ raise ImportError("pxr is not installed")
755
+ return Gf.Vec3d(self.x, self.y, self.z)
756
+
757
+ def to_quat(self) -> "Quaternion":
758
+ """
759
+ Convert RPY angles to quaternion.
760
+
761
+ Assumes this Vector3 contains roll-pitch-yaw angles in radians.
762
+
763
+ :return: Quaternion representation.
764
+ """
765
+
766
+ from common.message.quaternion import Quaternion
767
+
768
+ return Quaternion.from_rpy(self.x, self.y, self.z)
769
+
770
+ def to_point(self) -> Point3:
771
+ """
772
+ Convert to a Point3.
773
+
774
+ :return: A Point3 with the same x, y, z coordinates.
775
+ """
776
+
777
+ return Point3(x=self.x, y=self.y, z=self.z)
778
+
779
+ def as_array(self) -> list[float]:
780
+ """
781
+ Convert to a list (alias for to_list for Rust compatibility).
782
+
783
+ :return: The list of values.
784
+ """
785
+
786
+ return self.to_list()