forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
meshcat.html
155 lines (140 loc) · 5.12 KB
/
meshcat.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<!DOCTYPE html>
<!-- This file is forked from dist/index.html in rdeits/meshcat.-->
<html>
<head>
<meta charset=utf-8>
<title>Drake MeshCat</title>
</head>
<body>
<div id="status-message">
No connection to server.
</div>
<div id="meshcat-pane">
</div>
<script type="text/javascript" src="meshcat.js"></script>
<script type="text/javascript" src="stats.min.js"></script>
<script type="text/javascript" src="msgpack.min.js"></script>
<script>
// TODO(#16486): add tooltips to Stats to describe chart contents
var stats = new Stats();
var realtimeRatePanel = stats.addPanel(
new Stats.Panel('rtr%', '#ff8', '#221')
);
document.body.appendChild(stats.dom);
stats.dom.id = "stats-plot";
// We want to show the realtime rate panel by default
// it is the last element in the stats.dom.children list
stats.showPanel(stats.dom.children.length - 1)
var latestRealtimeRate = 0;
var viewer = new MeshCat.Viewer(document.getElementById("meshcat-pane"));
viewer.animate = function() {
viewer.animator.update();
if (viewer.needs_render) {
viewer.render();
}
}
function animate() {
stats.begin();
// convert realtime rate to percentage so it is easier to read
realtimeRatePanel.update(latestRealtimeRate*100, 100);
viewer.animate()
stats.end();
requestAnimationFrame(animate);
}
// TODO(#16486): Replace this function with more robust custom command
// handling in Meshcat
function handle_message(ws_message) {
let decoded = msgpack.decode(new Uint8Array(ws_message.data));
if (decoded.type == "realtime_rate") {
rtr = decoded.rate;
} else if (decoded.type == "show_realtime_rate") {
stats.dom.style.display = decoded.show ? "block" : "none";
} else {
viewer.handle_command(decoded)
}
}
requestAnimationFrame( animate );
// Set background to match Drake Visualizer.
viewer.set_property(['Background'], "top_color", [.95, .95, 1.0])
viewer.set_property(['Background'], "bottom_color", [.32, .32, .35])
// Set the initial view looking up the y-axis.
viewer.set_property(['Cameras', 'default', 'rotated', '<object>'],
"position", [0.0, 1.0, 3.0])
<!-- CONNECTION BLOCK BEGIN -->
// The lifespan of the server may be much shorter than this visualizer
// client. We'd like the user to not have to explicitly reload when they
// start a new server. So, we automatically try to reconnect at some given
// rate. However, due to the split of visualizer state between server and
// client, simply reconnecting may leave the *existing* visualizer in a
// strange state with various stale artifacts. So, when we detect a
// *reconnection*, we simply reload the page, so that every *meaningful*
// connection is accompanied by a fresh client state. Upon loading the
// page, it can accept a connection. After that first connection, any
// new connection is interpreted as a signal to reload.
var accepting_connections = true;
status_dom = document.getElementById("status-message");
// When the connection closes, try creating a new connection automatically.
function make_connection(url, reconnect_ms) {
try {
connection = new WebSocket(url);
connection.binaryType = "arraybuffer";
connection.onmessage = (msg) => handle_message(msg);
connection.onopen = (evt) => {
if (!accepting_connections) location.reload();
accepting_connections = false
};
connection.onclose = function(evt) {
status_dom.style.display = "block";
if (do_reconnect) {
// Immediately schedule an attempt to reconnect.
setTimeout(() => {make_connection(url, reconnect_ms);}, reconnect_ms);
}
}
viewer.connection = connection
} catch (e) {
console.info("Not connected to MeshCat websocket server: ", e);
if (do_reconnect) {
setTimeout(() => {make_connection(url, reconnect_ms);}, reconnect_ms);
}
}
}
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
reconnect_ms = parseInt(urlParams.get('reconnect_ms')) || 1000;
var do_reconnect = reconnect_ms > 0;
if (do_reconnect) {
status_dom.textContent = "No connection to server. Attempting to reconnect...";
}
url = location.toString();
url = url.replace("http://", "ws://")
url = url.replace("https://", "wss://")
url = url.replace("/index.html", "/")
url = url.replace("/meshcat.html", "/")
make_connection(url, reconnect_ms);
<!-- CONNECTION BLOCK END -->
</script>
<style>
body {
margin: 0;
}
#meshcat-pane {
width: 100vw;
height: 100vh;
overflow: hidden;
}
#status-message{
width: 50vw;
text-align: center;
font-weight: bold;
background-color: yellow;
position: fixed;
left: 25%;
display: none;
}
#stats-plot {
display: none;
}
</style>
<script id="embedded-json"></script>
</body>
</html>