Spaces:
Running
Running
File size: 6,295 Bytes
516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 f0f3d44 e2080a3 f0f3d44 516e6e9 e2080a3 f0f3d44 e2080a3 f0f3d44 e2080a3 f0f3d44 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 f0f3d44 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 516e6e9 e2080a3 f0f3d44 e2080a3 516e6e9 e2080a3 516e6e9 | 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 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: transparent; font-family: system-ui, sans-serif; color: #e8eaf0; }
svg text { font-family: system-ui, sans-serif; }
</style>
</head>
<body>
<svg id="action-rep" style="overflow:visible"></svg>
<script>
function _initActionRep() {
const svgEl = document.getElementById('action-rep');
const W = Math.min(svgEl.parentElement.clientWidth || 700, 720);
const H = 310;
svgEl.setAttribute('width', W);
svgEl.setAttribute('height', H);
const svg = d3.select('#action-rep').attr('width', W).attr('height', H);
svg.selectAll('*').remove();
const m = { top: 30, right: 24, bottom: 64, left: 48 };
const w = W - m.left - m.right;
const h = H - m.top - m.bottom;
const g = svg.append('g').attr('transform', `translate(${m.left},${m.top})`);
const SUB = '#8b8fa8';
const COL_REL = '#3b82f6';
const COL_DELTA = '#f59e0b';
const COL_ABS = '#ef4444';
// Arrowhead markers
[['rel', COL_REL], ['delta', COL_DELTA]].forEach(([id, col]) => {
svg.append('defs').append('marker')
.attr('id', `ah-${id}`).attr('viewBox', '0 0 8 6').attr('refX', 7).attr('refY', 3)
.attr('markerWidth', 8).attr('markerHeight', 6).attr('orient', 'auto')
.append('path').attr('d', 'M0,0.5 L7,3 L0,5.5 Z').attr('fill', col);
});
const poses = [
{t:0, p:0.8}, {t:1, p:1.5}, {t:2, p:2.1}, {t:3, p:2.9}, {t:4, p:3.3},
{t:5, p:4.0}, {t:6, p:4.5}, {t:7, p:5.1}, {t:8, p:5.6}
];
const x = d3.scaleLinear().domain([-0.3, 8.5]).range([0, w]);
const y = d3.scaleLinear().domain([0, 6.5]).range([h, 0]);
// Faint ground-truth path
const line = d3.line().x(d => x(d.t)).y(d => y(d.p)).curve(d3.curveMonotoneX);
g.append('path').datum(poses).attr('d', line)
.attr('fill', 'none').attr('stroke', '#3a3d4a').attr('stroke-width', 1.5).attr('stroke-dasharray', '4,4');
// Inference boundary
[0, 4].forEach(t => {
g.append('line').attr('x1', x(t)).attr('x2', x(t)).attr('y1', -14).attr('y2', h + 4)
.attr('stroke', '#444').attr('stroke-dasharray', '5,4').attr('stroke-width', 0.8);
});
g.append('text').attr('x', x(2)).attr('y', -4).attr('text-anchor', 'middle')
.attr('fill', SUB).attr('font-size', 11).text('Inference start at t=0');
g.append('text').attr('x', x(6)).attr('y', -4).attr('text-anchor', 'middle')
.attr('fill', SUB).attr('font-size', 11).text('Inference start at t=4');
// Axes
g.append('line').attr('x1', x(0)).attr('x2', x(8.3)).attr('y1', h).attr('y2', h).attr('stroke', '#444');
g.append('line').attr('x1', x(0)).attr('x2', x(0)).attr('y1', h).attr('y2', y(6.2)).attr('stroke', '#444');
for (let t = 0; t <= 8; t++) {
g.append('text').attr('x', x(t)).attr('y', h + 14).attr('text-anchor', 'middle')
.attr('fill', SUB).attr('font-size', 12).text(t);
}
g.append('text').attr('x', x(8.4)).attr('y', h + 4).attr('fill', SUB).attr('font-size', 12).text('Time');
g.append('text').attr('x', x(-0.1)).attr('y', y(6.4)).attr('text-anchor', 'end')
.attr('fill', SUB).attr('font-size', 12).text('Pose');
// --- DELTA (yellow): straight arrows, offset below trajectory ---
const dOff = 14;
for (let i = 0; i < poses.length - 1; i++) {
g.append('line')
.attr('x1', x(poses[i].t) + 3).attr('y1', y(poses[i].p) + dOff)
.attr('x2', x(poses[i+1].t) - 3).attr('y2', y(poses[i+1].p) + dOff)
.attr('stroke', COL_DELTA).attr('stroke-width', 2).attr('opacity', 0.8)
.attr('marker-end', 'url(#ah-delta)');
}
// --- RELATIVE (blue): curved arrows above trajectory ---
function curvedArrow(x1, y1, x2, y2) {
const cpY = Math.min(y1, y2) - 20 - Math.abs(x2 - x1) * 0.08;
const cpX = (x1 + x2) / 2;
g.append('path')
.attr('d', `M${x1},${y1 - 4} Q${cpX},${cpY} ${x2},${y2 - 4}`)
.attr('fill', 'none').attr('stroke', COL_REL).attr('stroke-width', 2).attr('opacity', 0.85)
.attr('marker-end', 'url(#ah-rel)');
}
// Chunk 1: t=0 → t=1,2,3,4
for (let i = 1; i <= 4; i++) curvedArrow(x(0), y(poses[0].p), x(i), y(poses[i].p));
// Chunk 2: t=4 → t=5,6,7,8
for (let i = 5; i <= 8; i++) curvedArrow(x(4), y(poses[4].p), x(i), y(poses[i].p));
// --- ABSOLUTE (red): dots on trajectory ---
poses.forEach(d => {
g.append('circle').attr('cx', x(d.t)).attr('cy', y(d.p)).attr('r', 5)
.attr('fill', COL_ABS).attr('stroke', '#fff').attr('stroke-width', 1.5);
});
// Chunk brackets
const bY = h + 22;
[{t0:0, t1:4}, {t0:4, t1:8}].forEach(({t0, t1}) => {
g.append('path')
.attr('d', `M${x(t0)},${bY-3} L${x(t0)},${bY} L${x(t1)},${bY} L${x(t1)},${bY-3}`)
.attr('fill', 'none').attr('stroke', COL_REL).attr('stroke-width', 1.2).attr('opacity', 0.5);
g.append('text').attr('x', (x(t0) + x(t1)) / 2).attr('y', bY + 12)
.attr('text-anchor', 'middle').attr('fill', COL_REL).attr('font-size', 11).attr('opacity', 0.7)
.text('chunk = 4');
});
// Legend (bottom)
const legY = h + 44;
const legItems = [
{ col: COL_REL, label: 'Relative trajectory (used here)', type: 'curve' },
{ col: COL_DELTA, label: 'Delta (accumulates error)', type: 'line' },
{ col: COL_ABS, label: 'Absolute', type: 'dot' },
];
let lx = 0;
legItems.forEach(item => {
const sw = 14;
if (item.type === 'curve') {
g.append('line').attr('x1', lx).attr('x2', lx + sw).attr('y1', legY + 2).attr('y2', legY + 2)
.attr('stroke', item.col).attr('stroke-width', 2.5);
} else if (item.type === 'line') {
g.append('line').attr('x1', lx).attr('x2', lx + sw).attr('y1', legY + 2).attr('y2', legY + 2)
.attr('stroke', item.col).attr('stroke-width', 2.5);
} else {
g.append('circle').attr('cx', lx + sw / 2).attr('cy', legY + 2).attr('r', 4.5)
.attr('fill', item.col).attr('stroke', '#fff').attr('stroke-width', 1.2);
}
lx += sw + 6;
const t = g.append('text').attr('x', lx).attr('y', legY + 6)
.attr('fill', item.col).attr('font-size', 12).attr('font-weight', 500).text(item.label);
lx += t.node().getComputedTextLength() + 16;
});
}
if (typeof d3 !== "undefined") { _initActionRep(); }
else { var s=document.createElement("script"); s.src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"; s.onload=_initActionRep; document.head.appendChild(s); }
window.addEventListener('resize', _initActionRep);
</script>
</body>
</html>
|