|
13 | 13 | _sw = None |
14 | 14 | sw = None |
15 | 15 |
|
| 16 | +SHAPE_ADD = 10000 |
16 | 17 |
|
17 | 18 | def _import_swift(): # pragma nocover |
18 | 19 | import importlib |
@@ -258,29 +259,70 @@ def add( |
258 | 259 | id = len(self.robots) |
259 | 260 |
|
260 | 261 | self.robots.append(robot_object) |
261 | | - return id |
| 262 | + return int(id) |
262 | 263 | elif isinstance(ob, rp.Shape): |
263 | 264 | shape = ob.to_dict() |
264 | 265 | if self.display: |
265 | 266 | id = self._send_socket('shape', shape) |
266 | 267 | else: |
267 | | - id = len(self.shapes) |
| 268 | + id = len(self.shapes) + SHAPE_ADD |
268 | 269 | self.shapes.append(ob) |
269 | | - return id |
| 270 | + return int(id) |
270 | 271 |
|
271 | | - def remove(self): |
| 272 | + def remove(self, id): |
272 | 273 | """ |
273 | | - Remove a robot to the graphical scene |
| 274 | + Remove a robot/shape from the graphical scene |
274 | 275 |
|
275 | 276 | ``env.remove(robot)`` removes the ``robot`` from the graphical |
276 | 277 | environment. |
277 | | - """ |
278 | 278 |
|
279 | | - # TODO - shouldn't this have an id argument? which robot does it remove |
280 | | - # TODO - it can remove any entity? |
| 279 | + :param id: the id of the object as returned by the ``add`` method, |
| 280 | + or the instance of the object |
| 281 | + :type id: Int, Robot or Shape |
| 282 | + """ |
281 | 283 |
|
282 | 284 | super().remove() |
283 | 285 |
|
| 286 | + # ob to remove |
| 287 | + idd = None |
| 288 | + code = None |
| 289 | + |
| 290 | + if isinstance(id, rp.ERobot): |
| 291 | + |
| 292 | + for i in range(len(self.robots)): |
| 293 | + if self.robots[i] is not None and id == self.robots[i]['ob']: |
| 294 | + idd = i |
| 295 | + code = 'remove_robot' |
| 296 | + self.robots[idd] = None |
| 297 | + break |
| 298 | + |
| 299 | + elif isinstance(id, rp.Shape): |
| 300 | + |
| 301 | + for i in range(len(self.shapes)): |
| 302 | + if self.shapes[i] is not None and id == self.shapes[i]: |
| 303 | + idd = i |
| 304 | + code = 'remove_shape' |
| 305 | + self.shapes[idd] = None |
| 306 | + break |
| 307 | + |
| 308 | + elif id >= SHAPE_ADD: |
| 309 | + # Number corresponding to shape ID |
| 310 | + idd = id - SHAPE_ADD |
| 311 | + code = 'remove_shape' |
| 312 | + self.shapes[idd] = None |
| 313 | + else: |
| 314 | + # Number corresponding to robot ID |
| 315 | + idd = id |
| 316 | + code = 'remove_robot' |
| 317 | + self.robots[idd] = None |
| 318 | + |
| 319 | + if idd is None: |
| 320 | + raise ValueError( |
| 321 | + 'the id argument does not correspond with ' |
| 322 | + 'a robot or shape in Swift') |
| 323 | + |
| 324 | + self._send_socket(code, idd) |
| 325 | + |
284 | 326 | def hold(self): # pragma: no cover |
285 | 327 | ''' |
286 | 328 | hold() keeps the browser tab open i.e. stops the browser tab from |
@@ -344,52 +386,56 @@ def stop_recording(self): |
344 | 386 | def _step_robots(self, dt): |
345 | 387 |
|
346 | 388 | for robot_object in self.robots: |
347 | | - robot = robot_object['ob'] |
| 389 | + if robot_object is not None: |
| 390 | + robot = robot_object['ob'] |
348 | 391 |
|
349 | | - if robot_object['readonly'] or robot.control_type == 'p': |
350 | | - pass # pragma: no cover |
| 392 | + if robot_object['readonly'] or robot.control_type == 'p': |
| 393 | + pass # pragma: no cover |
351 | 394 |
|
352 | | - elif robot.control_type == 'v': |
| 395 | + elif robot.control_type == 'v': |
353 | 396 |
|
354 | | - for i in range(robot.n): |
355 | | - robot.q[i] += robot.qd[i] * (dt) |
| 397 | + for i in range(robot.n): |
| 398 | + robot.q[i] += robot.qd[i] * (dt) |
356 | 399 |
|
357 | | - if np.any(robot.qlim[:, i] != 0) and \ |
358 | | - not np.any(np.isnan(robot.qlim[:, i])): |
359 | | - robot.q[i] = np.min([robot.q[i], robot.qlim[1, i]]) |
360 | | - robot.q[i] = np.max([robot.q[i], robot.qlim[0, i]]) |
| 400 | + if np.any(robot.qlim[:, i] != 0) and \ |
| 401 | + not np.any(np.isnan(robot.qlim[:, i])): |
| 402 | + robot.q[i] = np.min([robot.q[i], robot.qlim[1, i]]) |
| 403 | + robot.q[i] = np.max([robot.q[i], robot.qlim[0, i]]) |
361 | 404 |
|
362 | | - elif robot.control_type == 'a': |
363 | | - pass |
| 405 | + elif robot.control_type == 'a': |
| 406 | + pass |
364 | 407 |
|
365 | | - else: # pragma: no cover |
366 | | - # Should be impossible to reach |
367 | | - raise ValueError( |
368 | | - 'Invalid robot.control_type. ' |
369 | | - 'Must be one of \'p\', \'v\', or \'a\'') |
| 408 | + else: # pragma: no cover |
| 409 | + # Should be impossible to reach |
| 410 | + raise ValueError( |
| 411 | + 'Invalid robot.control_type. ' |
| 412 | + 'Must be one of \'p\', \'v\', or \'a\'') |
370 | 413 |
|
371 | 414 | def _step_shapes(self, dt): |
372 | 415 |
|
373 | 416 | for shape in self.shapes: |
| 417 | + if shape is not None: |
374 | 418 |
|
375 | | - T = shape.base |
376 | | - t = T.t.astype('float64') |
377 | | - r = T.rpy('rad').astype('float64') |
| 419 | + T = shape.base |
| 420 | + t = T.t.astype('float64') |
| 421 | + r = T.rpy('rad').astype('float64') |
378 | 422 |
|
379 | | - t += shape.v[:3] * dt |
380 | | - r += shape.v[3:] * dt |
| 423 | + t += shape.v[:3] * dt |
| 424 | + r += shape.v[3:] * dt |
381 | 425 |
|
382 | | - shape.base = sm.SE3(t) * sm.SE3.RPY(r) |
| 426 | + shape.base = sm.SE3(t) * sm.SE3.RPY(r) |
383 | 427 |
|
384 | 428 | def _draw_all(self): |
385 | 429 |
|
386 | 430 | for i in range(len(self.robots)): |
387 | | - self._send_socket( |
388 | | - 'robot_poses', [i, self.robots[i]['ob'].fk_dict()]) |
| 431 | + if self.robots[i] is not None: |
| 432 | + self._send_socket( |
| 433 | + 'robot_poses', [i, self.robots[i]['ob'].fk_dict()]) |
389 | 434 |
|
390 | 435 | for i in range(len(self.shapes)): |
391 | | - self._send_socket( |
392 | | - 'shape_poses', [i, self.shapes[i].fk_dict()]) |
| 436 | + if self.shapes[i] is not None: |
| 437 | + self._send_socket( |
| 438 | + 'shape_poses', [i, self.shapes[i].fk_dict()]) |
393 | 439 |
|
394 | 440 | def _send_socket(self, code, data=None): |
395 | 441 | msg = [code, data] |
|
0 commit comments