File size: 4,086 Bytes
e8d112c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8a22488
 
 
e8d112c
 
 
 
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
// The MJCF validator's batch: every asset it checked, its settle and drop replayed in 3D from MuJoCo's traces, and each
// check with what it measured (scripts/validate_mjcf.py).
import { SimViewer } from "./viewer.js";

const $ = (id) => document.getElementById(id);
const el = (tag, cls, text) => { const e = document.createElement(tag); if (cls) e.className = cls; if (text !== undefined) e.textContent = text; return e; };
const ICON = { pass: "✓", warn: "⚠", fail: "✗" };
const CLS = { pass: "ok", warn: "warn", fail: "bad" };
const RUNS = { settle: "Settle on the floor", drop: "Drop from 10 cm" };

export async function mount() {
  const index = await (await fetch("validator/index.json")).json();
  const v = new SimViewer($("va-view"), { onFrame: (st) => { $("va-label").hidden = !(st && st.label); $("va-label").textContent = (st && st.label) || ""; } });
  let report = null, run = "settle";
  const counts = { pass: 0, warn: 0, fail: 0 };
  index.forEach((a) => { counts[a.verdict] += 1; });
  $("va-sum").textContent = `${index.length} assets: ${counts.fail} fail, ${counts.warn} pass with warnings, ${counts.pass} pass.`;

  index.forEach((a, i) => {
    const b = el("button", "btn va-asset");
    b.append(el("span", `va-dot ${CLS[a.verdict]}`, ICON[a.verdict]), el("span", "", a.name.replace(/_/g, " ")));
    b.title = [a.fails.length ? `fails: ${a.fails.join(", ")}` : "", a.warns.length ? `warns: ${a.warns.join(", ")}` : ""].filter(Boolean).join("; ");
    b.onclick = () => open(i);
    $("va-assets").append(b);
  });
  for (const [k, lab] of Object.entries(RUNS)) {
    const b = el("button", "btn", lab);
    b.dataset.k = k;
    b.onclick = () => { run = k; play(); };
    $("va-runs").append(b);
  }
  $("va-replay").onclick = () => play();

  function play() {
    [...$("va-runs").children].forEach((b) => b.classList.toggle("pri", b.dataset.k === run));
    const t = report && report.traces && report.traces[run];
    if (t) v.play(t);
  }

  async function open(i) {
    const a = index[i];
    [...$("va-assets").children].forEach((b, j) => b.classList.toggle("pri", j === i));
    report = await (await fetch(`validator/${a.file}`)).json();
    if (report.scene) {
      const meta = await (await fetch(`validator/${report.scene.replace(/\.glb$/, ".scene.json")}`)).json();
      await v.load(`validator/${report.scene}`, meta);
      $("va-grid").textContent = v.gridLabel;
    }
    $("va-title").textContent = report.name.replace(/_/g, " ");
    $("va-facts").textContent = report.bodies !== undefined
      ? `${report.mjcf}: ${report.bodies} ${report.bodies === 1 ? "body" : "bodies"}, ${report.mass_kg} kg, ${report.joints} ${report.joints === 1 ? "joint" : "joints"}, a ${(report.timestep * 1000).toFixed(1)} ms step` + (report.floor_added ? ", on a floor the validator adds" : "")
      : report.mjcf;
    const t = $("va-checks");
    t.textContent = "";
    const head = el("tr");
    for (const h of ["", "Check", "What it found"]) head.append(el("th", "", h));
    t.append(head);
    for (const c of report.checks) {
      const tr = el("tr");
      tr.append(el("td", CLS[c.level], ICON[c.level]), el("td", "", c.id), el("td", "", c.message));
      t.append(tr);
    }
    $("va-verdict").textContent = `${report.verdict === "pass" ? "Passes every check" : report.verdict === "warn" ? "Passes, with warnings" : "Fails"}: ` +
      `${report.checks.filter((c) => c.level === "fail").length} fail, ${report.checks.filter((c) => c.level === "warn").length} warn, ${report.checks.filter((c) => c.level === "pass").length} pass`;
    $("va-verdict").className = "vr-verdict " + (report.verdict === "fail" ? "bad" : "ok");
    $("va-md").href = `validator/${a.file.replace(/\.json$/, ".md")}`;
    // a scene with furniture isn't dropped, so it has no drop to replay
    [...$("va-runs").children].forEach((b) => { b.disabled = !(report.traces && report.traces[b.dataset.k]); });
    if (!(report.traces && report.traces[run])) run = "settle";
    play();
  }
  await open(Math.max(0, index.findIndex((a) => a.verdict === "fail")));
}