diff --git a/examples/demo.ipynb b/examples/demo.ipynb index 5562863..17b7469 100644 --- a/examples/demo.ipynb +++ b/examples/demo.ipynb @@ -417,7 +417,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "tags": [] + }, "source": [ "## Cart-Pole\n", "\n", @@ -453,6 +455,47 @@ " time.sleep(0.01)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's set the camera position to above left of the cartpole:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vis.jupyter_cell()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vis.set_cam_pos([0.6, -1.0, 1.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's change the focus to the leftmost 'l' character:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vis.set_cam_target([0.0, -1.0, 0.0])" + ] + }, { "cell_type": "code", "execution_count": null, @@ -477,9 +520,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.9.13" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/src/meshcat/commands.py b/src/meshcat/commands.py index 3816461..a8ddcf6 100644 --- a/src/meshcat/commands.py +++ b/src/meshcat/commands.py @@ -44,12 +44,36 @@ def lower(self): u"matrix": list(self.matrix.T.flatten()) } -class CaptureImage: + +class SetCamTarget: + """Set the camera target point.""" + __slots__ = ["value"] + def __init__(self, pos): + self.value = pos def lower(self): return { + u"type": "set_target", + u"path": "", + u"value": list(self.value) + } + + +class CaptureImage: + + def __init__(self, xres=None, yres=None): + self.xres = xres + self.yres = yres + + def lower(self): + data = { u"type": u"capture_image" } + if self.xres: + data[u"xres"] = self.xres + if self.yres: + data[u"yres"] = self.yres + return data class Delete: diff --git a/src/meshcat/geometry.py b/src/meshcat/geometry.py index 2d1ebdb..ed9109d 100644 --- a/src/meshcat/geometry.py +++ b/src/meshcat/geometry.py @@ -427,7 +427,7 @@ def __init__(self, contents, mesh_format): def lower(self, object_data): return { - u"type": u"_meshfile", + u"type": u"_meshfile_geometry", u"uuid": self.uuid, u"format": self.mesh_format, u"data": self.contents diff --git a/src/meshcat/servers/zmqserver.py b/src/meshcat/servers/zmqserver.py index 38dc175..3b7e39a 100644 --- a/src/meshcat/servers/zmqserver.py +++ b/src/meshcat/servers/zmqserver.py @@ -259,6 +259,9 @@ def handle_zmq(self, frames): self.zmq_socket.send(self.web_url.encode("utf-8")) elif cmd == "wait": self.ioloop.add_callback(self.wait_for_websockets) + elif cmd == "set_target": + self.forward_to_websockets(frames) + self.zmq_socket.send(b"ok") elif cmd == "capture_image": if len(self.websocket_pool) > 0: self.forward_to_websockets(frames) # on_message callback should handle the pb diff --git a/src/meshcat/viewer b/src/meshcat/viewer index 978cb8f..65781fc 160000 --- a/src/meshcat/viewer +++ b/src/meshcat/viewer @@ -1 +1 @@ -Subproject commit 978cb8f519f9bb540e94b7f97a39ada4d7916b7c +Subproject commit 65781fcb064db536b99a66fe9fcf5bf0b6d1f790 diff --git a/src/meshcat/visualizer.py b/src/meshcat/visualizer.py index 23a425a..b7b9264 100644 --- a/src/meshcat/visualizer.py +++ b/src/meshcat/visualizer.py @@ -8,7 +8,7 @@ from .path import Path -from .commands import SetObject, SetTransform, Delete, SetProperty, SetAnimation, CaptureImage +from .commands import SetObject, SetTransform, Delete, SetProperty, SetAnimation, CaptureImage, SetCamTarget from .geometry import MeshPhongMaterial from .servers.zmqserver import start_zmq_server_as_subprocess @@ -68,8 +68,8 @@ def get_scene(self): # we receive the HTML as utf-8-encoded, so decode here return self.zmq_socket.recv().decode('utf-8') - def get_image(self): - cmd_data = CaptureImage().lower() + def get_image(self, w, h): + cmd_data = CaptureImage(w, h).lower() self.zmq_socket.send_multipart([ cmd_data["type"].encode("utf-8"), "".encode("utf-8"), @@ -157,9 +157,22 @@ def set_property(self, key, value): def set_animation(self, animation, play=True, repetitions=1): return self.window.send(SetAnimation(animation, play=play, repetitions=repetitions)) - def get_image(self): + def set_cam_target(self, value): + """Set camera target (in right-handed coordinates (x,y,z)).""" + v = list(value) + v[1], v[2] = v[2], -v[1] # convert to left-handed (x,z,-y) + return self.window.send(SetCamTarget(v)) + + def set_cam_pos(self, value): + """Set camera position (in right-handed coordinates (x,y,z)).""" + path = "/Cameras/default/rotated/" + v = list(value) + v[1], v[2] = v[2], -v[1] # convert to left-handed (x,z,-y) + return self[path].set_property("position", v) + + def get_image(self, w=None, h=None): """Save an image""" - return self.window.get_image() + return self.window.get_image(w, h) def delete(self): return self.window.send(Delete(self.path))