simview 3.2__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.
Files changed (163) hide show
  1. simview/__init__.py +69 -0
  2. simview/__main__.py +174 -0
  3. simview/launcher.py +109 -0
  4. simview/live.py +204 -0
  5. simview/merge.py +593 -0
  6. simview/model.py +690 -0
  7. simview/py.typed +0 -0
  8. simview/scene.py +603 -0
  9. simview/server.py +739 -0
  10. simview/state.py +190 -0
  11. simview/static/css/controls.css +36 -0
  12. simview/static/js/SimView.js +601 -0
  13. simview/static/js/components/AnimationController.js +654 -0
  14. simview/static/js/components/BatchManager.js +198 -0
  15. simview/static/js/components/InteractionController.js +446 -0
  16. simview/static/js/components/InteractionControls.js +116 -0
  17. simview/static/js/components/Scene.js +166 -0
  18. simview/static/js/components/StateStore.js +188 -0
  19. simview/static/js/config.js +233 -0
  20. simview/static/js/main.js +3 -0
  21. simview/static/js/objects/Body.js +616 -0
  22. simview/static/js/objects/StaticObject.js +181 -0
  23. simview/static/js/objects/Terrain.js +514 -0
  24. simview/static/js/objects/utils.js +378 -0
  25. simview/static/js/ui/AnalysisPanel.js +233 -0
  26. simview/static/js/ui/BatchLegend.js +207 -0
  27. simview/static/js/ui/BodyStateWindow.js +521 -0
  28. simview/static/js/ui/Controls.js +586 -0
  29. simview/static/js/ui/ErrorMetrics.js +664 -0
  30. simview/static/js/ui/Legend.js +87 -0
  31. simview/static/js/ui/PlaybackControls.js +314 -0
  32. simview/static/js/ui/ScalarPlotter.js +575 -0
  33. simview/static/js/utils/blobCodec.js +73 -0
  34. simview/static/js/utils/bodyTransforms.js +151 -0
  35. simview/static/js/utils/csv.js +46 -0
  36. simview/static/js/utils/errorMath.js +69 -0
  37. simview/static/js/utils/injectStyles.js +16 -0
  38. simview/static/js/utils/interpolate.js +65 -0
  39. simview/static/js/utils/liveFollow.js +17 -0
  40. simview/static/js/utils/loadRecordingLibs.js +34 -0
  41. simview/static/js/utils/viewState.js +226 -0
  42. simview/static/lib/chroma-js-3.1.2/index.min.js +8 -0
  43. simview/static/lib/chroma-js-3.1.2/src/Color.js +49 -0
  44. simview/static/lib/chroma-js-3.1.2/src/chroma.js +10 -0
  45. simview/static/lib/chroma-js-3.1.2/src/colors/colorbrewer.js +81 -0
  46. simview/static/lib/chroma-js-3.1.2/src/colors/w3cx11.js +164 -0
  47. simview/static/lib/chroma-js-3.1.2/src/generator/average.js +88 -0
  48. simview/static/lib/chroma-js-3.1.2/src/generator/bezier.js +86 -0
  49. simview/static/lib/chroma-js-3.1.2/src/generator/blend.js +57 -0
  50. simview/static/lib/chroma-js-3.1.2/src/generator/cubehelix.js +87 -0
  51. simview/static/lib/chroma-js-3.1.2/src/generator/mix.js +19 -0
  52. simview/static/lib/chroma-js-3.1.2/src/generator/random.js +12 -0
  53. simview/static/lib/chroma-js-3.1.2/src/generator/scale.js +394 -0
  54. simview/static/lib/chroma-js-3.1.2/src/interpolator/_hsx.js +59 -0
  55. simview/static/lib/chroma-js-3.1.2/src/interpolator/hcg.js +12 -0
  56. simview/static/lib/chroma-js-3.1.2/src/interpolator/hsi.js +12 -0
  57. simview/static/lib/chroma-js-3.1.2/src/interpolator/hsl.js +12 -0
  58. simview/static/lib/chroma-js-3.1.2/src/interpolator/hsv.js +12 -0
  59. simview/static/lib/chroma-js-3.1.2/src/interpolator/index.js +1 -0
  60. simview/static/lib/chroma-js-3.1.2/src/interpolator/lab.js +19 -0
  61. simview/static/lib/chroma-js-3.1.2/src/interpolator/lch.js +13 -0
  62. simview/static/lib/chroma-js-3.1.2/src/interpolator/lrgb.js +19 -0
  63. simview/static/lib/chroma-js-3.1.2/src/interpolator/num.js +15 -0
  64. simview/static/lib/chroma-js-3.1.2/src/interpolator/oklab.js +19 -0
  65. simview/static/lib/chroma-js-3.1.2/src/interpolator/oklch.js +12 -0
  66. simview/static/lib/chroma-js-3.1.2/src/interpolator/rgb.js +18 -0
  67. simview/static/lib/chroma-js-3.1.2/src/io/cmyk/cmyk2rgb.js +16 -0
  68. simview/static/lib/chroma-js-3.1.2/src/io/cmyk/index.js +27 -0
  69. simview/static/lib/chroma-js-3.1.2/src/io/cmyk/rgb2cmyk.js +17 -0
  70. simview/static/lib/chroma-js-3.1.2/src/io/css/css2rgb.js +238 -0
  71. simview/static/lib/chroma-js-3.1.2/src/io/css/hsl2css.js +26 -0
  72. simview/static/lib/chroma-js-3.1.2/src/io/css/index.js +27 -0
  73. simview/static/lib/chroma-js-3.1.2/src/io/css/lab2css.js +24 -0
  74. simview/static/lib/chroma-js-3.1.2/src/io/css/lch2css.js +24 -0
  75. simview/static/lib/chroma-js-3.1.2/src/io/css/oklab2css.js +16 -0
  76. simview/static/lib/chroma-js-3.1.2/src/io/css/oklch2css.js +16 -0
  77. simview/static/lib/chroma-js-3.1.2/src/io/css/rgb2css.js +61 -0
  78. simview/static/lib/chroma-js-3.1.2/src/io/gl/index.js +22 -0
  79. simview/static/lib/chroma-js-3.1.2/src/io/hcg/hcg2rgb.js +55 -0
  80. simview/static/lib/chroma-js-3.1.2/src/io/hcg/index.js +27 -0
  81. simview/static/lib/chroma-js-3.1.2/src/io/hcg/rgb2hcg.js +23 -0
  82. simview/static/lib/chroma-js-3.1.2/src/io/hex/hex2rgb.js +56 -0
  83. simview/static/lib/chroma-js-3.1.2/src/io/hex/index.js +29 -0
  84. simview/static/lib/chroma-js-3.1.2/src/io/hex/rgb2hex.js +29 -0
  85. simview/static/lib/chroma-js-3.1.2/src/io/hsi/hsi2rgb.js +45 -0
  86. simview/static/lib/chroma-js-3.1.2/src/io/hsi/index.js +27 -0
  87. simview/static/lib/chroma-js-3.1.2/src/io/hsi/rgb2hsi.js +31 -0
  88. simview/static/lib/chroma-js-3.1.2/src/io/hsl/hsl2rgb.js +35 -0
  89. simview/static/lib/chroma-js-3.1.2/src/io/hsl/index.js +27 -0
  90. simview/static/lib/chroma-js-3.1.2/src/io/hsl/rgb2hsl.js +45 -0
  91. simview/static/lib/chroma-js-3.1.2/src/io/hsv/hsv2rgb.js +47 -0
  92. simview/static/lib/chroma-js-3.1.2/src/io/hsv/index.js +27 -0
  93. simview/static/lib/chroma-js-3.1.2/src/io/hsv/rgb2hsv.js +32 -0
  94. simview/static/lib/chroma-js-3.1.2/src/io/input.js +4 -0
  95. simview/static/lib/chroma-js-3.1.2/src/io/lab/index.js +28 -0
  96. simview/static/lib/chroma-js-3.1.2/src/io/lab/lab-constants.js +120 -0
  97. simview/static/lib/chroma-js-3.1.2/src/io/lab/lab2rgb.js +101 -0
  98. simview/static/lib/chroma-js-3.1.2/src/io/lab/rgb2lab.js +67 -0
  99. simview/static/lib/chroma-js-3.1.2/src/io/lch/hcl2rgb.js +9 -0
  100. simview/static/lib/chroma-js-3.1.2/src/io/lch/index.js +35 -0
  101. simview/static/lib/chroma-js-3.1.2/src/io/lch/lab2lch.js +12 -0
  102. simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2lab.js +18 -0
  103. simview/static/lib/chroma-js-3.1.2/src/io/lch/lch2rgb.js +13 -0
  104. simview/static/lib/chroma-js-3.1.2/src/io/lch/rgb2lch.js +12 -0
  105. simview/static/lib/chroma-js-3.1.2/src/io/named/index.js +30 -0
  106. simview/static/lib/chroma-js-3.1.2/src/io/num/index.js +32 -0
  107. simview/static/lib/chroma-js-3.1.2/src/io/num/num2rgb.js +13 -0
  108. simview/static/lib/chroma-js-3.1.2/src/io/num/rgb2num.js +8 -0
  109. simview/static/lib/chroma-js-3.1.2/src/io/oklab/index.js +27 -0
  110. simview/static/lib/chroma-js-3.1.2/src/io/oklab/oklab2rgb.js +34 -0
  111. simview/static/lib/chroma-js-3.1.2/src/io/oklab/rgb2oklab.js +37 -0
  112. simview/static/lib/chroma-js-3.1.2/src/io/oklch/index.js +27 -0
  113. simview/static/lib/chroma-js-3.1.2/src/io/oklch/oklch2rgb.js +13 -0
  114. simview/static/lib/chroma-js-3.1.2/src/io/oklch/rgb2oklch.js +12 -0
  115. simview/static/lib/chroma-js-3.1.2/src/io/rgb/index.js +44 -0
  116. simview/static/lib/chroma-js-3.1.2/src/io/temp/index.js +22 -0
  117. simview/static/lib/chroma-js-3.1.2/src/io/temp/rgb2temperature.js +30 -0
  118. simview/static/lib/chroma-js-3.1.2/src/io/temp/temperature2rgb.js +39 -0
  119. simview/static/lib/chroma-js-3.1.2/src/ops/alpha.js +13 -0
  120. simview/static/lib/chroma-js-3.1.2/src/ops/clipped.js +5 -0
  121. simview/static/lib/chroma-js-3.1.2/src/ops/darken.js +17 -0
  122. simview/static/lib/chroma-js-3.1.2/src/ops/get.js +13 -0
  123. simview/static/lib/chroma-js-3.1.2/src/ops/luminance.js +54 -0
  124. simview/static/lib/chroma-js-3.1.2/src/ops/mix.js +10 -0
  125. simview/static/lib/chroma-js-3.1.2/src/ops/premultiply.js +12 -0
  126. simview/static/lib/chroma-js-3.1.2/src/ops/saturate.js +15 -0
  127. simview/static/lib/chroma-js-3.1.2/src/ops/set.js +43 -0
  128. simview/static/lib/chroma-js-3.1.2/src/ops/shade.js +11 -0
  129. simview/static/lib/chroma-js-3.1.2/src/utils/analyze.js +191 -0
  130. simview/static/lib/chroma-js-3.1.2/src/utils/clip_rgb.js +15 -0
  131. simview/static/lib/chroma-js-3.1.2/src/utils/contrast.js +12 -0
  132. simview/static/lib/chroma-js-3.1.2/src/utils/contrastAPCA.js +68 -0
  133. simview/static/lib/chroma-js-3.1.2/src/utils/delta-e.js +62 -0
  134. simview/static/lib/chroma-js-3.1.2/src/utils/distance.js +17 -0
  135. simview/static/lib/chroma-js-3.1.2/src/utils/index.js +27 -0
  136. simview/static/lib/chroma-js-3.1.2/src/utils/last.js +8 -0
  137. simview/static/lib/chroma-js-3.1.2/src/utils/limit.js +5 -0
  138. simview/static/lib/chroma-js-3.1.2/src/utils/multiply-matrices.js +36 -0
  139. simview/static/lib/chroma-js-3.1.2/src/utils/scales.js +15 -0
  140. simview/static/lib/chroma-js-3.1.2/src/utils/type.js +18 -0
  141. simview/static/lib/chroma-js-3.1.2/src/utils/unpack.js +17 -0
  142. simview/static/lib/chroma-js-3.1.2/src/utils/valid.js +11 -0
  143. simview/static/lib/chroma-js-3.1.2/src/version.js +2 -0
  144. simview/static/lib/download.js +132 -0
  145. simview/static/lib/js-colormaps.js +19335 -0
  146. simview/static/lib/tar.js +334 -0
  147. simview/static/lib/three-0.174.0/addons/controls/OrbitControls.js +1556 -0
  148. simview/static/lib/three-0.174.0/addons/libs/lil-gui.module.min.js +8 -0
  149. simview/static/lib/three-0.174.0/three.core.js +48830 -0
  150. simview/static/lib/three-0.174.0/three.module.js +17313 -0
  151. simview/static/lib/uPlot.esm.js +6140 -0
  152. simview/static/lib/uPlot.min.css +2 -0
  153. simview/static/textures/contacts/red-cross0.png +0 -0
  154. simview/static/textures/points/ball0.png +0 -0
  155. simview/static/textures/points/ball1.png +0 -0
  156. simview/templates/index.html +81 -0
  157. simview/utils.py +41 -0
  158. simview-3.2.dist-info/METADATA +552 -0
  159. simview-3.2.dist-info/RECORD +163 -0
  160. simview-3.2.dist-info/WHEEL +5 -0
  161. simview-3.2.dist-info/entry_points.txt +2 -0
  162. simview-3.2.dist-info/licenses/LICENSE +28 -0
  163. simview-3.2.dist-info/top_level.txt +1 -0
simview/state.py ADDED
@@ -0,0 +1,190 @@
1
+ from collections.abc import Sequence
2
+ from dataclasses import dataclass
3
+
4
+ import numpy as np
5
+ import torch
6
+
7
+ from simview.model import OptionalBodyStateAttribute, _encode_blob
8
+
9
+ # Accepted array-like input types for authoring calls: torch tensors and numpy
10
+ # arrays are both supported so callers don't need torch just to build a scene.
11
+ ArrayLike = torch.Tensor | np.ndarray
12
+
13
+ # Accepted input types for a body's constant `local_transform`: unlike the
14
+ # ArrayLike-typed per-frame pose/vector fields above (which call `.tolist()`
15
+ # unconditionally and so require an actual tensor/ndarray), `local_transform`
16
+ # is only ever normalized via `hasattr(..., "tolist")` + `list(...)` (see
17
+ # SimViewBody.create), so a plain sequence of floats works too and callers
18
+ # shouldn't need numpy/torch just to pass a constant 7-element offset.
19
+ LocalTransformLike = torch.Tensor | np.ndarray | Sequence[float]
20
+
21
+ # Maps a BodyTrajectory field name to the wire key used in each state's body dict.
22
+ # These are the numeric per-body fields that add_trajectory can pack as binary
23
+ # float32 blobs; contacts are ragged integer lists and stay as JSON.
24
+ TRAJECTORY_VECTOR_FIELDS = {
25
+ "velocity": OptionalBodyStateAttribute.VELOCITY.value,
26
+ "angular_velocity": OptionalBodyStateAttribute.ANGULAR_VELOCITY.value,
27
+ "force": OptionalBodyStateAttribute.FORCE.value,
28
+ "torque": OptionalBodyStateAttribute.TORQUE.value,
29
+ }
30
+
31
+ # Wire keys SimViewBodyState.to_json() may pack as binary float32 blobs, same
32
+ # set add_trajectory packs. Contacts are ragged (per-batch index lists) and
33
+ # always stay plain JSON.
34
+ _BINARY_ELIGIBLE_FIELDS = {"bodyTransform", *TRAJECTORY_VECTOR_FIELDS.values()}
35
+
36
+
37
+ @dataclass
38
+ class BodyTrajectory:
39
+ """A whole-timeline pose (and optional vectors) for one body.
40
+
41
+ Shapes are ``(T, B, k)`` — T timesteps, B batches — or ``(T, k)`` when the
42
+ scene has a single batch. Orientations are ``[w, x, y, z]`` (scalar-first),
43
+ matching the rest of SimView. Pass a list of these to
44
+ :meth:`SimulationScene.add_trajectory` to append an entire time-series in one
45
+ call instead of building a ``SimViewBodyState`` per frame.
46
+
47
+ ``name`` may be a list of body names instead of a single string, to cover
48
+ several bodies that move rigidly together (e.g. links welded to the same
49
+ parent). All named bodies must already exist in the model, and the
50
+ transform/vectors here are applied identically to each of them, avoiding
51
+ the need to duplicate the same data per body.
52
+
53
+ If the named body has a ``parent`` set in the model (see
54
+ ``SimulationScene.create_body``), ``positions``/``orientations`` here are
55
+ interpreted as local to that parent's current-frame pose instead of world
56
+ space. A body with a constant ``local_transform`` on the model instead
57
+ must never appear in a ``BodyTrajectory`` at all.
58
+ """
59
+
60
+ name: str | list[str]
61
+ positions: ArrayLike # (T, B, 3) or (T, 3)
62
+ orientations: ArrayLike # (T, B, 4) or (T, 4), [w, x, y, z]
63
+ velocity: ArrayLike | None = None # (T, B, 3) or (T, 3)
64
+ angular_velocity: ArrayLike | None = None
65
+ force: ArrayLike | None = None
66
+ torque: ArrayLike | None = None
67
+ # Optional per-timestep contacts: a sequence of length T, where each entry is
68
+ # anything SimViewBodyState._process_contacts accepts for one frame (a (B, N)
69
+ # boolean/float mask, a (B, N) integer index tensor/array, or a list of
70
+ # per-batch index lists). Kept separate from the numeric vector fields above
71
+ # since contacts are ragged and always shipped as plain JSON, never binary.
72
+ contacts: list | None = None
73
+
74
+
75
+ class SimViewBodyState:
76
+ def __init__(
77
+ self,
78
+ body_name: str | list[str],
79
+ position: ArrayLike,
80
+ orientation: ArrayLike,
81
+ optional_attributes: dict | None = None,
82
+ binary: bool = True,
83
+ ):
84
+ """``body_name`` may be a list of body names sharing this exact
85
+ transform (and any optional attributes), for bodies that move
86
+ rigidly together — see :class:`BodyTrajectory` for the same idea
87
+ applied to whole trajectories.
88
+
89
+ If the named body has a ``parent`` set in the model, ``position``/
90
+ ``orientation`` here are interpreted as local to that parent's
91
+ current-frame pose instead of world space (see
92
+ ``SimulationScene.create_body``).
93
+
94
+ With ``binary=True`` (the default, matching
95
+ ``SimulationScene.add_trajectory``), ``bodyTransform`` and any
96
+ provided vector attributes (velocity/angularVelocity/force/torque)
97
+ are packed as float32 ``__b64__`` blobs, shrinking the output file;
98
+ the viewer and :func:`merge_simulation_files` decode these
99
+ transparently. Set ``binary=False`` to emit plain JSON lists."""
100
+ self.body_name = body_name
101
+ self.position = position.tolist()
102
+ self.orientation = orientation.tolist()
103
+ self.binary = binary
104
+ self._set_optional_attributes(optional_attributes or {})
105
+
106
+ def __repr__(self) -> str:
107
+ return f"SimViewBodyState(name={self.body_name}, position={self.position}, orientation={self.orientation}, optional_attrs={self.optional_attrs})"
108
+
109
+ def _set_optional_attributes(self, attrs):
110
+ self.optional_attrs = {}
111
+ for key, value in attrs.items():
112
+ try:
113
+ key = OptionalBodyStateAttribute(key)
114
+ except ValueError:
115
+ raise ValueError(f"Unknown optional attribute: {key}")
116
+ if key == OptionalBodyStateAttribute.CONTACTS:
117
+ value = self._process_contacts(value)
118
+ elif isinstance(value, (torch.Tensor, np.ndarray)):
119
+ value = value.tolist()
120
+ elif isinstance(value, list):
121
+ if all(isinstance(v, (torch.Tensor, np.ndarray)) for v in value):
122
+ value = [v.tolist() for v in value]
123
+ elif all(isinstance(v, list) for v in value):
124
+ pass
125
+ else:
126
+ raise ValueError("Unknown list format")
127
+ else:
128
+ raise ValueError(f"Unknown attribute type for {key}: {type(value)}")
129
+ self.optional_attrs[key.value] = value
130
+
131
+ @staticmethod
132
+ def _process_contacts(contacts: ArrayLike | list):
133
+ if isinstance(contacts, (torch.Tensor, np.ndarray)): # tensor / array
134
+ if isinstance(contacts, torch.Tensor):
135
+ dtype = contacts.dtype
136
+ is_bool = dtype == torch.bool
137
+ is_float = dtype.is_floating_point
138
+ is_complex = dtype.is_complex
139
+ if is_bool or is_float:
140
+ # Boolean mask (floats treated as a mask of non-zero entries)
141
+ return [
142
+ torch.nonzero(c, as_tuple=True)[0].tolist() for c in contacts
143
+ ]
144
+ elif not is_complex: # integer dtype: assume indices
145
+ return contacts.tolist()
146
+ else:
147
+ raise ValueError(f"Unsupported contact tensor dtype: {dtype}")
148
+ else:
149
+ np_dtype = contacts.dtype
150
+ is_bool = np_dtype == np.bool_
151
+ is_float = np.issubdtype(np_dtype, np.floating)
152
+ is_complex = np.issubdtype(np_dtype, np.complexfloating)
153
+ if is_bool or is_float:
154
+ # Boolean mask (floats treated as a mask of non-zero entries)
155
+ return [np.nonzero(c)[0].tolist() for c in contacts]
156
+ elif not is_complex: # integer dtype: assume indices
157
+ return contacts.tolist()
158
+ else:
159
+ raise ValueError(f"Unsupported contact tensor dtype: {np_dtype}")
160
+ else: # list of tensors/arrays or list of lists
161
+ first = contacts[0]
162
+ if isinstance(first, (torch.Tensor, np.ndarray)):
163
+ return [SimViewBodyState._process_contacts(c) for c in contacts]
164
+ elif isinstance(first, list):
165
+ return contacts
166
+ else:
167
+ raise ValueError("Unknown contact format")
168
+
169
+ def to_json(self):
170
+ # Merge position and orientation into bodyTransform
171
+ # position: [x, y, z] or [[x, y, z], ...]
172
+ # orientation: [w, x, y, z] or [[w, x, y, z], ...]
173
+ if len(self.position) > 0 and isinstance(self.position[0], list):
174
+ # Batched
175
+ body_transform = [p + o for p, o in zip(self.position, self.orientation)]
176
+ else:
177
+ # Single
178
+ body_transform = self.position + self.orientation
179
+
180
+ fields = {"bodyTransform": body_transform, **self.optional_attrs}
181
+ if self.binary:
182
+ fields = {
183
+ key: (
184
+ _encode_blob(np.array(value, dtype=np.float32))
185
+ if key in _BINARY_ELIGIBLE_FIELDS
186
+ else value
187
+ )
188
+ for key, value in fields.items()
189
+ }
190
+ return {"name": self.body_name, **fields}
@@ -0,0 +1,36 @@
1
+ select {
2
+ background-color: #444;
3
+ color: white;
4
+ border: none;
5
+ border-radius: 4px;
6
+ padding: 5px;
7
+ cursor: pointer;
8
+ }
9
+
10
+ select:hover {
11
+ background-color: #555;
12
+ }
13
+
14
+ button:hover {
15
+ background-color: #555;
16
+ }
17
+
18
+ button:active {
19
+ background-color: #666;
20
+ }
21
+
22
+ /* Add styles for the loading splash screen */
23
+ #loading-splash {
24
+ position: fixed;
25
+ top: 0;
26
+ left: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ background-color: #000;
30
+ color: #fff;
31
+ display: flex;
32
+ justify-content: center;
33
+ align-items: center;
34
+ z-index: 10000;
35
+ font-family: sans-serif;
36
+ }