wirepod-vector-sdk-audio 0.9.0__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.
- anki_vector/__init__.py +43 -0
- anki_vector/animation.py +272 -0
- anki_vector/annotate.py +590 -0
- anki_vector/audio.py +212 -0
- anki_vector/audio_stream.py +335 -0
- anki_vector/behavior.py +1135 -0
- anki_vector/camera.py +670 -0
- anki_vector/camera_viewer/__init__.py +121 -0
- anki_vector/color.py +88 -0
- anki_vector/configure/__main__.py +331 -0
- anki_vector/connection.py +838 -0
- anki_vector/events.py +420 -0
- anki_vector/exceptions.py +185 -0
- anki_vector/faces.py +819 -0
- anki_vector/lights.py +210 -0
- anki_vector/mdns.py +131 -0
- anki_vector/messaging/__init__.py +45 -0
- anki_vector/messaging/alexa_pb2.py +36 -0
- anki_vector/messaging/alexa_pb2_grpc.py +3 -0
- anki_vector/messaging/behavior_pb2.py +40 -0
- anki_vector/messaging/behavior_pb2_grpc.py +3 -0
- anki_vector/messaging/client.py +33 -0
- anki_vector/messaging/cube_pb2.py +113 -0
- anki_vector/messaging/cube_pb2_grpc.py +3 -0
- anki_vector/messaging/extensions_pb2.py +25 -0
- anki_vector/messaging/extensions_pb2_grpc.py +3 -0
- anki_vector/messaging/external_interface_pb2.py +169 -0
- anki_vector/messaging/external_interface_pb2_grpc.py +1267 -0
- anki_vector/messaging/messages_pb2.py +431 -0
- anki_vector/messaging/messages_pb2_grpc.py +3 -0
- anki_vector/messaging/nav_map_pb2.py +33 -0
- anki_vector/messaging/nav_map_pb2_grpc.py +3 -0
- anki_vector/messaging/protocol.py +33 -0
- anki_vector/messaging/response_status_pb2.py +27 -0
- anki_vector/messaging/response_status_pb2_grpc.py +3 -0
- anki_vector/messaging/settings_pb2.py +72 -0
- anki_vector/messaging/settings_pb2_grpc.py +3 -0
- anki_vector/messaging/shared_pb2.py +54 -0
- anki_vector/messaging/shared_pb2_grpc.py +3 -0
- anki_vector/motors.py +127 -0
- anki_vector/nav_map.py +409 -0
- anki_vector/objects.py +1782 -0
- anki_vector/opengl/__init__.py +103 -0
- anki_vector/opengl/assets/LICENSE.txt +21 -0
- anki_vector/opengl/assets/cube.jpg +0 -0
- anki_vector/opengl/assets/cube.mtl +9 -0
- anki_vector/opengl/assets/cube.obj +1000 -0
- anki_vector/opengl/assets/vector.mtl +67 -0
- anki_vector/opengl/assets/vector.obj +13220 -0
- anki_vector/opengl/opengl.py +864 -0
- anki_vector/opengl/opengl_vector.py +620 -0
- anki_vector/opengl/opengl_viewer.py +689 -0
- anki_vector/photos.py +145 -0
- anki_vector/proximity.py +176 -0
- anki_vector/reserve_control/__main__.py +36 -0
- anki_vector/robot.py +930 -0
- anki_vector/screen.py +201 -0
- anki_vector/status.py +322 -0
- anki_vector/touch.py +119 -0
- anki_vector/user_intent.py +186 -0
- anki_vector/util.py +1132 -0
- anki_vector/version.py +15 -0
- anki_vector/viewer.py +403 -0
- anki_vector/vision.py +202 -0
- anki_vector/world.py +899 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/METADATA +80 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/RECORD +71 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/WHEEL +5 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/licenses/LICENSE.txt +180 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/top_level.txt +1 -0
- wirepod_vector_sdk_audio-0.9.0.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
# Copyright (c) 2018 Anki, Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License in the file LICENSE.txt or at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""This module provides Vector-specific 3D support classes for OpenGL, used by opengl_viewer.py.
|
|
16
|
+
|
|
17
|
+
Warning:
|
|
18
|
+
This package requires Python to have the PyOpenGL package installed, along
|
|
19
|
+
with an implementation of GLUT (OpenGL Utility Toolkit).
|
|
20
|
+
|
|
21
|
+
To install the Python packages on Mac and Linux do ``python3 -m pip install --user "wirepod_vector_sdk[3dviewer]"``
|
|
22
|
+
|
|
23
|
+
To install the Python packages on Windows do ``py -3 -m pip install --user "wirepod_vector_sdk[3dviewer]"``
|
|
24
|
+
|
|
25
|
+
On Windows and Linux you must also install freeglut (macOS / OSX has one
|
|
26
|
+
preinstalled).
|
|
27
|
+
|
|
28
|
+
On Linux: ``sudo apt-get install freeglut3``
|
|
29
|
+
|
|
30
|
+
On Windows: Go to http://freeglut.sourceforge.net/ to get a ``freeglut.dll``
|
|
31
|
+
file. It's included in any of the `Windows binaries` downloads. Place the DLL
|
|
32
|
+
next to your Python script, or install it somewhere in your PATH to allow any
|
|
33
|
+
script to use it."
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
# __all__ should order by constants, event classes, other classes, functions.
|
|
37
|
+
__all__ = ['CubeRenderFrame', 'FaceRenderFrame', 'LightCubeView', 'RobotRenderFrame', 'RobotView',
|
|
38
|
+
'UnitCubeView', 'VectorViewManifest', 'WorldRenderFrame']
|
|
39
|
+
|
|
40
|
+
import math
|
|
41
|
+
import time
|
|
42
|
+
from typing import List
|
|
43
|
+
|
|
44
|
+
from anki_vector.faces import Face
|
|
45
|
+
from anki_vector.objects import CustomObject, FixedCustomObject, LightCube, ObservableObject
|
|
46
|
+
from anki_vector import nav_map, util
|
|
47
|
+
from . import opengl
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
from OpenGL.GL import (GL_AMBIENT, GL_BLEND, GL_COMPILE, GL_DIFFUSE, GL_FILL, GL_FRONT, GL_FRONT_AND_BACK, GL_LIGHTING, GL_LINE, GL_LINE_STRIP,
|
|
51
|
+
GL_ONE_MINUS_SRC_ALPHA, GL_POLYGON, GL_SHININESS, GL_SPECULAR, GL_SRC_ALPHA, GL_TRIANGLE_STRIP,
|
|
52
|
+
glBegin, glBlendFunc, glCallList, glColor, glColor3f, glColor4f, glDisable, glEnable, glEnd, glEndList, glGenLists,
|
|
53
|
+
glMaterialfv, glMultMatrixf, glNewList, glNormal3fv, glPolygonMode, glPopMatrix, glPushMatrix, glRotatef, glScalef,
|
|
54
|
+
glTranslatef, glVertex3f, glVertex3fv)
|
|
55
|
+
|
|
56
|
+
except ImportError as import_exc:
|
|
57
|
+
opengl.raise_opengl_or_pillow_import_error(import_exc)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
#: The object file used to render the robot.
|
|
61
|
+
VECTOR_MODEL_FILE = "vector.obj"
|
|
62
|
+
|
|
63
|
+
#: The object file used to render the cube.
|
|
64
|
+
CUBE_MODEL_FILE = "cube.obj"
|
|
65
|
+
|
|
66
|
+
# The following offsets are used in displaying the Vector 3d model.
|
|
67
|
+
# These values are tuned to reflect the vector.obj file, and do not
|
|
68
|
+
# necessarily reflect the actual measurements of the physical robot.
|
|
69
|
+
|
|
70
|
+
#: The length of Vector's lift arm
|
|
71
|
+
LIFT_ARM_LENGTH_MM = 66.0
|
|
72
|
+
|
|
73
|
+
#: The height above ground of Vector's lift arm's pivot
|
|
74
|
+
LIFT_PIVOT_HEIGHT_MM = 45.0
|
|
75
|
+
|
|
76
|
+
#: Angle of the lift in the object's initial default pose.
|
|
77
|
+
LIFT_ANGLE_IN_DEFAULT_POSE = -0.1136
|
|
78
|
+
|
|
79
|
+
#: Pivot offset for where the fork rotates around itself
|
|
80
|
+
FORK_PIVOT_X = 3.0
|
|
81
|
+
FORK_PIVOT_Z = 3.4
|
|
82
|
+
|
|
83
|
+
#: Offset for the axel that the upper arm rotates around.
|
|
84
|
+
UPPER_ARM_PIVOT_X = -3.73
|
|
85
|
+
UPPER_ARM_PIVOT_Z = 4.47
|
|
86
|
+
|
|
87
|
+
#: Offset for the axel that the lower arm rotates around.
|
|
88
|
+
LOWER_ARM_PIVOT_X = -3.74
|
|
89
|
+
LOWER_ARM_PIVOT_Z = 3.27
|
|
90
|
+
|
|
91
|
+
#: Offset for the pivot that the head rotates around.
|
|
92
|
+
HEAD_PIVOT_X = -1.1
|
|
93
|
+
HEAD_PIVOT_Z = 4.75
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
_resource_package = __name__ # All resources are in subdirectories from this file's location
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class UnitCubeView(opengl.PrecomputedView):
|
|
100
|
+
"""A view containing a cube of unit size at the origin."""
|
|
101
|
+
|
|
102
|
+
def __init__(self):
|
|
103
|
+
|
|
104
|
+
self._display_list_name = 'cube'
|
|
105
|
+
|
|
106
|
+
super(UnitCubeView, self).__init__()
|
|
107
|
+
self.build_from_render_function(self._display_list_name, self._render_cube)
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _render_cube():
|
|
111
|
+
"""Pre renders a unit-size cube, with normals, centered at the origin.
|
|
112
|
+
"""
|
|
113
|
+
# build each of the 6 faces
|
|
114
|
+
for face_index in range(6):
|
|
115
|
+
# calculate normal and vertices for this face
|
|
116
|
+
vertex_normal = [0.0, 0.0, 0.0]
|
|
117
|
+
vertex_pos_options1 = [-1.0, 1.0, 1.0, -1.0]
|
|
118
|
+
vertex_pos_options2 = [1.0, 1.0, -1.0, -1.0]
|
|
119
|
+
face_index_even = ((face_index % 2) == 0)
|
|
120
|
+
# odd and even faces point in opposite directions
|
|
121
|
+
normal_dir = 1.0 if face_index_even else -1.0
|
|
122
|
+
if face_index < 2:
|
|
123
|
+
# -X and +X faces (vert positions differ in Y,Z)
|
|
124
|
+
vertex_normal[0] = normal_dir
|
|
125
|
+
v1i = 1
|
|
126
|
+
v2i = 2
|
|
127
|
+
elif face_index < 4:
|
|
128
|
+
# -Y and +Y faces (vert positions differ in X,Z)
|
|
129
|
+
vertex_normal[1] = normal_dir
|
|
130
|
+
v1i = 0
|
|
131
|
+
v2i = 2
|
|
132
|
+
else:
|
|
133
|
+
# -Z and +Z faces (vert positions differ in X,Y)
|
|
134
|
+
vertex_normal[2] = normal_dir
|
|
135
|
+
v1i = 0
|
|
136
|
+
v2i = 1
|
|
137
|
+
|
|
138
|
+
vertex_pos = list(vertex_normal)
|
|
139
|
+
|
|
140
|
+
# Polygon (N verts) with optional normals and tex coords
|
|
141
|
+
glBegin(GL_POLYGON)
|
|
142
|
+
for vert_index in range(4):
|
|
143
|
+
vertex_pos[v1i] = vertex_pos_options1[vert_index]
|
|
144
|
+
vertex_pos[v2i] = vertex_pos_options2[vert_index]
|
|
145
|
+
glNormal3fv(vertex_normal)
|
|
146
|
+
glVertex3fv(vertex_pos)
|
|
147
|
+
glEnd()
|
|
148
|
+
|
|
149
|
+
def display(self, color: List[float], draw_solid: bool):
|
|
150
|
+
"""Displays the cube with a specific color.
|
|
151
|
+
|
|
152
|
+
:param color: Color to display the cube.
|
|
153
|
+
:param draw_solid: Whether to draw solid polygons (False to draw wireframe).
|
|
154
|
+
"""
|
|
155
|
+
glColor(color)
|
|
156
|
+
|
|
157
|
+
if draw_solid:
|
|
158
|
+
ambient_color = [color[0] * 0.1, color[1] * 0.1, color[2] * 0.1, 1.0]
|
|
159
|
+
else:
|
|
160
|
+
ambient_color = color
|
|
161
|
+
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_color)
|
|
162
|
+
glMaterialfv(GL_FRONT, GL_DIFFUSE, color)
|
|
163
|
+
glMaterialfv(GL_FRONT, GL_SPECULAR, color)
|
|
164
|
+
|
|
165
|
+
glMaterialfv(GL_FRONT, GL_SHININESS, 10.0)
|
|
166
|
+
|
|
167
|
+
if draw_solid:
|
|
168
|
+
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
|
|
169
|
+
else:
|
|
170
|
+
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
|
|
171
|
+
|
|
172
|
+
self.display_by_key(self._display_list_name)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class LightCubeView(opengl.PrecomputedView):
|
|
176
|
+
"""A view containing the Vector Light Cube 3D Model.
|
|
177
|
+
|
|
178
|
+
:param mesh_data: Source Mesh Data for the light cube.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
def __init__(self, mesh_data: opengl.MeshData):
|
|
182
|
+
|
|
183
|
+
super(LightCubeView, self).__init__()
|
|
184
|
+
self.build_from_mesh_data(mesh_data)
|
|
185
|
+
|
|
186
|
+
def display(self, pose: util.Pose):
|
|
187
|
+
"""Displays the precomputed view at a specific pose in 3d space.
|
|
188
|
+
|
|
189
|
+
:param pose: Where to display the cube.
|
|
190
|
+
"""
|
|
191
|
+
glPushMatrix()
|
|
192
|
+
|
|
193
|
+
# TODO if cube_pose.is_accurate is False, render half-translucent?
|
|
194
|
+
# (This would require using a shader, or having duplicate objects)
|
|
195
|
+
|
|
196
|
+
cube_matrix = pose.to_matrix()
|
|
197
|
+
glMultMatrixf(cube_matrix.in_row_order)
|
|
198
|
+
|
|
199
|
+
# Cube is drawn slightly larger than the 10mm to 1 cm scale, as the model looks small otherwise
|
|
200
|
+
cube_scale_amt = 10.7
|
|
201
|
+
glScalef(cube_scale_amt, cube_scale_amt, cube_scale_amt)
|
|
202
|
+
|
|
203
|
+
self.display_all()
|
|
204
|
+
glPopMatrix()
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class RobotView(opengl.PrecomputedView):
|
|
208
|
+
"""A view containing the Vector robot 3D Model.
|
|
209
|
+
|
|
210
|
+
:param mesh_data: Source Mesh Data for the robot.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
def __init__(self, mesh_data: opengl.MeshData):
|
|
214
|
+
|
|
215
|
+
super(RobotView, self).__init__()
|
|
216
|
+
self.build_from_mesh_data(mesh_data)
|
|
217
|
+
|
|
218
|
+
def _display_vector_body(self):
|
|
219
|
+
"""Displays the robot's body to the current OpenGL context
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
# Render the static body meshes - first the main body:
|
|
223
|
+
self.display_by_key("body_geo")
|
|
224
|
+
# Render the left treads and wheels
|
|
225
|
+
self.display_by_key("trackBase_L_geo")
|
|
226
|
+
self.display_by_key("wheel_BL_geo")
|
|
227
|
+
self.display_by_key("wheel_FL_geo")
|
|
228
|
+
self.display_by_key("tracks_L_geo")
|
|
229
|
+
# Render the right treads and wheels
|
|
230
|
+
self.display_by_key("trackBase_R_geo")
|
|
231
|
+
self.display_by_key("wheel_BR_geo")
|
|
232
|
+
self.display_by_key("wheel_FR_geo")
|
|
233
|
+
self.display_by_key("tracks_R_geo")
|
|
234
|
+
|
|
235
|
+
def _display_vector_lift(self, lift_angle: float):
|
|
236
|
+
"""Displays the robot's lift to the current OpenGL context
|
|
237
|
+
|
|
238
|
+
:param lift_angle: the angle of the lift in radians
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
# Render the fork at the front (but not the arms)
|
|
242
|
+
glPushMatrix()
|
|
243
|
+
# The fork rotates first around upper arm (to get it to the correct position).
|
|
244
|
+
glTranslatef(UPPER_ARM_PIVOT_X, 0.0, UPPER_ARM_PIVOT_Z)
|
|
245
|
+
glRotatef(lift_angle, 0, 1, 0)
|
|
246
|
+
glTranslatef(-UPPER_ARM_PIVOT_X, 0.0, -UPPER_ARM_PIVOT_Z)
|
|
247
|
+
# The fork then rotates back around itself as it always hangs vertically.
|
|
248
|
+
glTranslatef(FORK_PIVOT_X, 0.0, FORK_PIVOT_Z)
|
|
249
|
+
glRotatef(-lift_angle, 0, 1, 0)
|
|
250
|
+
glTranslatef(-FORK_PIVOT_X, 0.0, -FORK_PIVOT_Z)
|
|
251
|
+
# Render
|
|
252
|
+
self.display_by_key("fork_geo")
|
|
253
|
+
glPopMatrix()
|
|
254
|
+
|
|
255
|
+
# Render the upper arms:
|
|
256
|
+
glPushMatrix()
|
|
257
|
+
# Rotate the upper arms around the upper arm joint
|
|
258
|
+
glTranslatef(UPPER_ARM_PIVOT_X, 0.0, UPPER_ARM_PIVOT_Z)
|
|
259
|
+
glRotatef(lift_angle, 0, 1, 0)
|
|
260
|
+
glTranslatef(-UPPER_ARM_PIVOT_X, 0.0, -UPPER_ARM_PIVOT_Z)
|
|
261
|
+
# Render
|
|
262
|
+
self.display_by_key("uprArm_L_geo")
|
|
263
|
+
self.display_by_key("uprArm_geo")
|
|
264
|
+
glPopMatrix()
|
|
265
|
+
|
|
266
|
+
# Render the lower arms:
|
|
267
|
+
glPushMatrix()
|
|
268
|
+
# Rotate the lower arms around the lower arm joint
|
|
269
|
+
glTranslatef(LOWER_ARM_PIVOT_X, 0.0, LOWER_ARM_PIVOT_Z)
|
|
270
|
+
glRotatef(lift_angle, 0, 1, 0)
|
|
271
|
+
glTranslatef(-LOWER_ARM_PIVOT_X, 0.0, -LOWER_ARM_PIVOT_Z)
|
|
272
|
+
# Render
|
|
273
|
+
self.display_by_key("lwrArm_L_geo")
|
|
274
|
+
self.display_by_key("lwrArm_R_geo")
|
|
275
|
+
glPopMatrix()
|
|
276
|
+
|
|
277
|
+
def _display_vector_head(self, head_angle: float):
|
|
278
|
+
"""Displays the robot's head to the current OpenGL context
|
|
279
|
+
|
|
280
|
+
:param head_angle: the angle of the lift in radians
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
glPushMatrix()
|
|
284
|
+
# Rotate the head around the pivot
|
|
285
|
+
glTranslatef(HEAD_PIVOT_X, 0.0, HEAD_PIVOT_Z)
|
|
286
|
+
glRotatef(-head_angle, 0, 1, 0)
|
|
287
|
+
glTranslatef(-HEAD_PIVOT_X, 0.0, -HEAD_PIVOT_Z)
|
|
288
|
+
# Render all of the head meshes
|
|
289
|
+
self.display_by_key("head_geo")
|
|
290
|
+
# Screen
|
|
291
|
+
self.display_by_key("backScreen_mat")
|
|
292
|
+
self.display_by_key("screenEdge_geo")
|
|
293
|
+
self.display_by_key("overscan_1_geo")
|
|
294
|
+
# Eyes
|
|
295
|
+
self.display_by_key("eye_L_geo")
|
|
296
|
+
self.display_by_key("eye_R_geo")
|
|
297
|
+
# Eyelids
|
|
298
|
+
self.display_by_key("eyeLid_R_top_geo")
|
|
299
|
+
self.display_by_key("eyeLid_L_top_geo")
|
|
300
|
+
self.display_by_key("eyeLid_L_btm_geo")
|
|
301
|
+
self.display_by_key("eyeLid_R_btm_geo")
|
|
302
|
+
# Face cover (drawn last as it's translucent):
|
|
303
|
+
self.display_by_key("front_Screen_geo")
|
|
304
|
+
glPopMatrix()
|
|
305
|
+
|
|
306
|
+
def display(self, pose: util.Pose, head_angle: util.Angle, lift_position: util.Distance):
|
|
307
|
+
"""Displays the precomputed view at a specific pose in 3d space.
|
|
308
|
+
|
|
309
|
+
:param pose: Where to display the robot.
|
|
310
|
+
"""
|
|
311
|
+
if not self._display_lists:
|
|
312
|
+
return
|
|
313
|
+
|
|
314
|
+
robot_matrix = pose.to_matrix()
|
|
315
|
+
head_angle_degrees = head_angle.degrees
|
|
316
|
+
|
|
317
|
+
# Get the angle of Vector's lift for rendering - we subtract the angle
|
|
318
|
+
# of the lift in the default pose in the object, and apply the inverse
|
|
319
|
+
# rotation
|
|
320
|
+
sin_angle = (lift_position.distance_mm - LIFT_PIVOT_HEIGHT_MM) / LIFT_ARM_LENGTH_MM
|
|
321
|
+
angle_radians = math.asin(sin_angle)
|
|
322
|
+
|
|
323
|
+
lift_angle = -(angle_radians - LIFT_ANGLE_IN_DEFAULT_POSE)
|
|
324
|
+
lift_angle_degrees = math.degrees(lift_angle)
|
|
325
|
+
|
|
326
|
+
glPushMatrix()
|
|
327
|
+
glEnable(GL_LIGHTING)
|
|
328
|
+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
329
|
+
glEnable(GL_BLEND)
|
|
330
|
+
|
|
331
|
+
glMultMatrixf(robot_matrix.in_row_order)
|
|
332
|
+
|
|
333
|
+
robot_scale_amt = 10.0 # cm to mm
|
|
334
|
+
glScalef(robot_scale_amt, robot_scale_amt, robot_scale_amt)
|
|
335
|
+
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
|
|
336
|
+
|
|
337
|
+
self._display_vector_body()
|
|
338
|
+
self._display_vector_lift(lift_angle_degrees)
|
|
339
|
+
self._display_vector_head(head_angle_degrees)
|
|
340
|
+
|
|
341
|
+
glDisable(GL_LIGHTING)
|
|
342
|
+
glPopMatrix()
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class NavMapView(opengl.PrecomputedView):
|
|
346
|
+
"""A view containing a cube of unit size at the origin."""
|
|
347
|
+
|
|
348
|
+
def __init__(self):
|
|
349
|
+
self.logger = util.get_class_logger(__name__, self)
|
|
350
|
+
super(NavMapView, self).__init__()
|
|
351
|
+
|
|
352
|
+
def build_from_nav_map(self, new_nav_map: nav_map.NavMapGrid):
|
|
353
|
+
"""Reconstructs the display list for the NavMapView based on a :class:`anki_vector.nav_map.NavMapGrid` object.
|
|
354
|
+
|
|
355
|
+
:param new_nav_map: nav map source data to be referenced for the new display list.
|
|
356
|
+
"""
|
|
357
|
+
cen = new_nav_map.center
|
|
358
|
+
half_size = new_nav_map.size * 0.5
|
|
359
|
+
|
|
360
|
+
self._display_lists['_navmap'] = glGenLists(1) # pylint: disable=assignment-from-no-return
|
|
361
|
+
glNewList(self._display_lists['_navmap'], GL_COMPILE)
|
|
362
|
+
|
|
363
|
+
glPushMatrix()
|
|
364
|
+
|
|
365
|
+
color_light_gray = (0.65, 0.65, 0.65)
|
|
366
|
+
glColor3f(*color_light_gray)
|
|
367
|
+
glBegin(GL_LINE_STRIP)
|
|
368
|
+
glVertex3f(cen.x + half_size, cen.y + half_size, cen.z) # TL
|
|
369
|
+
glVertex3f(cen.x + half_size, cen.y - half_size, cen.z) # TR
|
|
370
|
+
glVertex3f(cen.x - half_size, cen.y - half_size, cen.z) # BR
|
|
371
|
+
glVertex3f(cen.x - half_size, cen.y + half_size, cen.z) # BL
|
|
372
|
+
glVertex3f(cen.x + half_size, cen.y + half_size,
|
|
373
|
+
cen.z) # TL (close loop)
|
|
374
|
+
glEnd()
|
|
375
|
+
|
|
376
|
+
def color_for_content(content):
|
|
377
|
+
nct = nav_map.NavNodeContentTypes
|
|
378
|
+
colors = {nct.Unknown.value: (0.3, 0.3, 0.3), # dark gray
|
|
379
|
+
nct.ClearOfObstacle.value: (0.0, 1.0, 0.0), # green
|
|
380
|
+
nct.ClearOfCliff.value: (0.0, 0.5, 0.0), # dark green
|
|
381
|
+
nct.ObstacleCube.value: (1.0, 0.0, 0.0), # red
|
|
382
|
+
nct.ObstacleProximity.value: (1.0, 0.5, 0.0), # orange
|
|
383
|
+
nct.ObstacleProximityExplored.value: (0.5, 1.0, 0.0), # yellow-green
|
|
384
|
+
nct.ObstacleUnrecognized.value: (0.5, 0.0, 0.0), # dark red
|
|
385
|
+
nct.Cliff.value: (0.0, 0.0, 0.0), # black
|
|
386
|
+
nct.InterestingEdge.value: (1.0, 1.0, 0.0), # yellow
|
|
387
|
+
nct.NonInterestingEdge.value: (0.5, 0.5, 0.0), # dark-yellow
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
col = colors.get(content)
|
|
391
|
+
if col is None:
|
|
392
|
+
col = (1.0, 1.0, 1.0) # white
|
|
393
|
+
return col
|
|
394
|
+
|
|
395
|
+
fill_z = cen.z - 0.4
|
|
396
|
+
|
|
397
|
+
def _recursive_draw(grid_node: nav_map.NavMapGridNode):
|
|
398
|
+
if grid_node.children is not None:
|
|
399
|
+
for child in grid_node.children:
|
|
400
|
+
_recursive_draw(child)
|
|
401
|
+
else:
|
|
402
|
+
# leaf node - render as a quad
|
|
403
|
+
map_alpha = 0.5
|
|
404
|
+
cen = grid_node.center
|
|
405
|
+
half_size = grid_node.size * 0.5
|
|
406
|
+
|
|
407
|
+
# Draw outline
|
|
408
|
+
glColor4f(*color_light_gray, 1.0) # fully opaque
|
|
409
|
+
glBegin(GL_LINE_STRIP)
|
|
410
|
+
glVertex3f(cen.x + half_size, cen.y + half_size, cen.z)
|
|
411
|
+
glVertex3f(cen.x + half_size, cen.y - half_size, cen.z)
|
|
412
|
+
glVertex3f(cen.x - half_size, cen.y - half_size, cen.z)
|
|
413
|
+
glVertex3f(cen.x - half_size, cen.y + half_size, cen.z)
|
|
414
|
+
glVertex3f(cen.x + half_size, cen.y + half_size, cen.z)
|
|
415
|
+
glEnd()
|
|
416
|
+
|
|
417
|
+
# Draw filled contents
|
|
418
|
+
glColor4f(*color_for_content(grid_node.content), map_alpha)
|
|
419
|
+
glBegin(GL_TRIANGLE_STRIP)
|
|
420
|
+
glVertex3f(cen.x + half_size, cen.y - half_size, fill_z)
|
|
421
|
+
glVertex3f(cen.x + half_size, cen.y + half_size, fill_z)
|
|
422
|
+
glVertex3f(cen.x - half_size, cen.y - half_size, fill_z)
|
|
423
|
+
glVertex3f(cen.x - half_size, cen.y + half_size, fill_z)
|
|
424
|
+
glEnd()
|
|
425
|
+
|
|
426
|
+
_recursive_draw(new_nav_map.root_node)
|
|
427
|
+
|
|
428
|
+
glPopMatrix()
|
|
429
|
+
glEndList()
|
|
430
|
+
|
|
431
|
+
def display(self):
|
|
432
|
+
"""Displays the precomputed nav map view.
|
|
433
|
+
This function will do nothing if no display list has yet been built.
|
|
434
|
+
"""
|
|
435
|
+
if '_navmap' in self._display_lists:
|
|
436
|
+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
437
|
+
glEnable(GL_BLEND)
|
|
438
|
+
glPushMatrix()
|
|
439
|
+
glCallList(self._display_lists['_navmap'])
|
|
440
|
+
glPopMatrix()
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class VectorViewManifest():
|
|
444
|
+
"""A collection of Vector-specific source data containing views to display.
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
def __init__(self):
|
|
448
|
+
self._light_cube_view: LightCubeView = None
|
|
449
|
+
self._unit_cube_view: UnitCubeView = None
|
|
450
|
+
self._robot_view: RobotView = None
|
|
451
|
+
self._nav_map_view: NavMapView = None
|
|
452
|
+
|
|
453
|
+
@property
|
|
454
|
+
def light_cube_view(self) -> LightCubeView:
|
|
455
|
+
"""A precomputed view of Vector's light cube."""
|
|
456
|
+
return self._light_cube_view
|
|
457
|
+
|
|
458
|
+
@property
|
|
459
|
+
def unit_cube_view(self) -> UnitCubeView:
|
|
460
|
+
"""A precomputed view of a unit cube.
|
|
461
|
+
|
|
462
|
+
This is used for representing detected faces.
|
|
463
|
+
"""
|
|
464
|
+
return self._unit_cube_view
|
|
465
|
+
|
|
466
|
+
@property
|
|
467
|
+
def robot_view(self) -> RobotView:
|
|
468
|
+
"""A precomputed view of the robot."""
|
|
469
|
+
return self._robot_view
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def nav_map_view(self) -> NavMapView:
|
|
473
|
+
"""A precomputable view of the navigation map. This will be updated
|
|
474
|
+
as new content comes in.
|
|
475
|
+
"""
|
|
476
|
+
return self._nav_map_view
|
|
477
|
+
|
|
478
|
+
def load_assets(self):
|
|
479
|
+
"""Loads all assets needed for the view manifest, and precomputes them
|
|
480
|
+
into cached views.
|
|
481
|
+
"""
|
|
482
|
+
resource_context = opengl.ResourceManager(_resource_package)
|
|
483
|
+
|
|
484
|
+
# Load 3D objects
|
|
485
|
+
robot_mesh_data = opengl.MeshData(resource_context, VECTOR_MODEL_FILE)
|
|
486
|
+
self._robot_view = RobotView(robot_mesh_data)
|
|
487
|
+
|
|
488
|
+
# Load the cube
|
|
489
|
+
cube_mesh_data = opengl.MeshData(resource_context, CUBE_MODEL_FILE)
|
|
490
|
+
self._light_cube_view = LightCubeView(cube_mesh_data)
|
|
491
|
+
|
|
492
|
+
self._unit_cube_view = UnitCubeView()
|
|
493
|
+
|
|
494
|
+
self._nav_map_view = NavMapView()
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
class ObservableObjectRenderFrame(): # pylint: disable=too-few-public-methods
|
|
498
|
+
"""Minimal copy of an object's state for 1 frame of rendering.
|
|
499
|
+
|
|
500
|
+
:param obj: the cube object to be rendered.
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
def __init__(self, obj: ObservableObject):
|
|
504
|
+
self.pose = obj.pose
|
|
505
|
+
self.is_visible = obj.is_visible
|
|
506
|
+
self.last_observed_time = obj.last_observed_time
|
|
507
|
+
|
|
508
|
+
@property
|
|
509
|
+
def time_since_last_seen(self) -> float:
|
|
510
|
+
# Equivalent of ObservableObject's method
|
|
511
|
+
"""time since this obj was last seen (math.inf if never)"""
|
|
512
|
+
if self.last_observed_time is None:
|
|
513
|
+
return math.inf
|
|
514
|
+
return time.time() - self.last_observed_time
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
class CubeRenderFrame(ObservableObjectRenderFrame): # pylint: disable=too-few-public-methods
|
|
518
|
+
"""Minimal copy of a Cube's state for 1 frame of rendering.
|
|
519
|
+
|
|
520
|
+
:param cube: the cube object to be rendered.
|
|
521
|
+
"""
|
|
522
|
+
|
|
523
|
+
def __init__(self, cube: LightCube): # pylint: disable=useless-super-delegation
|
|
524
|
+
super().__init__(cube)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
class FaceRenderFrame(ObservableObjectRenderFrame): # pylint: disable=too-few-public-methods
|
|
528
|
+
"""Minimal copy of a Face's state for 1 frame of rendering.
|
|
529
|
+
|
|
530
|
+
:param face: The face object to be rendered.
|
|
531
|
+
"""
|
|
532
|
+
|
|
533
|
+
def __init__(self, face: Face): # pylint: disable=useless-super-delegation
|
|
534
|
+
super().__init__(face)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
class CustomObjectRenderFrame(ObservableObjectRenderFrame): # pylint: disable=too-few-public-methods
|
|
538
|
+
"""Minimal copy of a CustomObject's state for 1 frame of rendering.
|
|
539
|
+
|
|
540
|
+
:param custom_object: The custom object to be rendered. Either :class:`anki_vector.objects.CustomObject` or :class:`anki_vector.objects.FixedCustomObject`.
|
|
541
|
+
:param is_fixed: Whether the custom object is permanently defined rather than an observable archetype.
|
|
542
|
+
"""
|
|
543
|
+
|
|
544
|
+
def __init__(self, custom_object, is_fixed: bool):
|
|
545
|
+
if is_fixed:
|
|
546
|
+
# Not an observable, so init directly
|
|
547
|
+
self.pose = custom_object.pose
|
|
548
|
+
self.is_visible = None
|
|
549
|
+
self.last_observed_time = None
|
|
550
|
+
else:
|
|
551
|
+
super().__init__(custom_object)
|
|
552
|
+
|
|
553
|
+
self.is_fixed = is_fixed
|
|
554
|
+
|
|
555
|
+
if self.is_fixed:
|
|
556
|
+
self.x_size_mm = custom_object.x_size_mm
|
|
557
|
+
self.y_size_mm = custom_object.y_size_mm
|
|
558
|
+
self.z_size_mm = custom_object.z_size_mm
|
|
559
|
+
else:
|
|
560
|
+
self.x_size_mm = custom_object.archetype.x_size_mm
|
|
561
|
+
self.y_size_mm = custom_object.archetype.y_size_mm
|
|
562
|
+
self.z_size_mm = custom_object.archetype.z_size_mm
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class RobotRenderFrame(): # pylint: disable=too-few-public-methods
|
|
566
|
+
"""Minimal copy of a Robot's state for 1 frame of rendering.
|
|
567
|
+
|
|
568
|
+
:param robot: the robot object to be rendered.
|
|
569
|
+
"""
|
|
570
|
+
|
|
571
|
+
def __init__(self, robot):
|
|
572
|
+
self.pose = robot.pose
|
|
573
|
+
if robot.head_angle_rad is None:
|
|
574
|
+
self.head_angle = util.radians(0.0)
|
|
575
|
+
else:
|
|
576
|
+
self.head_angle = util.radians(robot.head_angle_rad)
|
|
577
|
+
if robot.lift_height_mm is None:
|
|
578
|
+
self.lift_position = util.distance_mm(0.0)
|
|
579
|
+
else:
|
|
580
|
+
self.lift_position = util.distance_mm(robot.lift_height_mm)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class WorldRenderFrame(): # pylint: disable=too-few-public-methods
|
|
584
|
+
"""Minimal copy of the World's state for 1 frame of rendering.
|
|
585
|
+
|
|
586
|
+
:param robot: the robot object to be rendered, which also has handles to the other objects
|
|
587
|
+
defined in it's world class.
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
def __init__(self, robot, connecting_to_cube):
|
|
591
|
+
|
|
592
|
+
self.connected_cube = robot.world.connected_light_cube is not None
|
|
593
|
+
self.connecting_to_cube = connecting_to_cube
|
|
594
|
+
self.robot_frame = RobotRenderFrame(robot)
|
|
595
|
+
|
|
596
|
+
self.cube_frames: List[CubeRenderFrame] = []
|
|
597
|
+
if robot.world.connected_light_cube is not None:
|
|
598
|
+
self.cube_frames.append(CubeRenderFrame(robot.world.connected_light_cube))
|
|
599
|
+
|
|
600
|
+
self.face_frames: List[FaceRenderFrame] = []
|
|
601
|
+
for face in robot.world.visible_faces:
|
|
602
|
+
# Ignore faces that have a newer version (with updated id)
|
|
603
|
+
# or if they haven't been seen in a while.
|
|
604
|
+
if not face.has_updated_face_id and (face.time_since_last_seen < 60):
|
|
605
|
+
self.face_frames.append(FaceRenderFrame(face))
|
|
606
|
+
|
|
607
|
+
self.custom_object_frames = []
|
|
608
|
+
for obj in robot.world.all_objects:
|
|
609
|
+
is_custom = isinstance(obj, CustomObject)
|
|
610
|
+
is_fixed = isinstance(obj, FixedCustomObject)
|
|
611
|
+
if is_custom or is_fixed:
|
|
612
|
+
self.custom_object_frames.append(CustomObjectRenderFrame(obj, is_fixed))
|
|
613
|
+
|
|
614
|
+
def cube_connected(self):
|
|
615
|
+
'''Is there a light cube connected to Vector'''
|
|
616
|
+
return self.connected_cube
|
|
617
|
+
|
|
618
|
+
def cube_connecting(self):
|
|
619
|
+
'''Is there a current attempt to connect to a light cube'''
|
|
620
|
+
return self.connecting_to_cube
|