Add linkage annotation overlay toggle and joint labels
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
- body = red
|
||||
- Play/pause, reset, speed slider, angle scrubber.
|
||||
- Foot trajectory traces for both sides.
|
||||
- Optional linkage annotation overlay (joint and link labels).
|
||||
- Diagnostics panel with closure error and fallback counters.
|
||||
|
||||
## File layout
|
||||
@@ -47,4 +48,3 @@ Then open:
|
||||
- No collision/ground dynamics and no body translation.
|
||||
- Side B is driven by `theta + pi`.
|
||||
- Lower branch of each circle-circle intersection is selected to maintain leg-down posture.
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@
|
||||
<span>Show foot traces</span>
|
||||
</label>
|
||||
|
||||
<label class="check">
|
||||
<input id="show-annotations" type="checkbox" />
|
||||
<span>Overlay linkage annotations</span>
|
||||
</label>
|
||||
|
||||
<section class="diag">
|
||||
<h2>Diagnostics</h2>
|
||||
<pre id="diag">loading…</pre>
|
||||
@@ -57,4 +62,3 @@
|
||||
<script type="module" src="./src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ const state = {
|
||||
showNear: true,
|
||||
showFar: true,
|
||||
showTrace: true,
|
||||
showAnnotations: false,
|
||||
fallbackCount: 0,
|
||||
pose: null,
|
||||
ground: {
|
||||
|
||||
@@ -160,6 +160,52 @@ function drawHUD(ctx, data) {
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawLabel(ctx, p, text, dx = 0, dy = 0) {
|
||||
ctx.save();
|
||||
ctx.font = '12px ui-monospace, SFMono-Regular, Menlo, monospace';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
const x = p.x + dx;
|
||||
const y = p.y + dy;
|
||||
const padX = 4;
|
||||
const padY = 2;
|
||||
const metrics = ctx.measureText(text);
|
||||
const w = metrics.width + padX * 2;
|
||||
const h = 14 + padY * 2;
|
||||
|
||||
ctx.fillStyle = 'rgba(10, 12, 18, 0.72)';
|
||||
ctx.fillRect(x - w * 0.5, y - h * 0.5, w, h);
|
||||
|
||||
ctx.fillStyle = '#f4f7ff';
|
||||
ctx.fillText(text, x, y + 0.5);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawAnnotations(ctx, tf, side) {
|
||||
if (!side) return;
|
||||
|
||||
const A = tf.toScreen(side.A);
|
||||
const B = tf.toScreen(side.B);
|
||||
const O = tf.toScreen(side.O);
|
||||
const C = tf.toScreen(side.C);
|
||||
const D = tf.toScreen(side.D);
|
||||
const E = tf.toScreen(side.E);
|
||||
const F = tf.toScreen(side.F);
|
||||
const G = tf.toScreen(side.G);
|
||||
|
||||
// Joint labels
|
||||
drawLabel(ctx, A, 'A', -12, -10);
|
||||
drawLabel(ctx, B, 'B', 12, -10);
|
||||
drawLabel(ctx, O, 'O', 0, -14);
|
||||
drawLabel(ctx, C, 'C', 0, 14);
|
||||
drawLabel(ctx, D, 'D', -12, 10);
|
||||
drawLabel(ctx, E, 'E', 12, 10);
|
||||
drawLabel(ctx, F, 'F', -12, 12);
|
||||
drawLabel(ctx, G, 'G', 12, 12);
|
||||
|
||||
}
|
||||
|
||||
export function renderScene(canvas, state) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = COLORS.bg;
|
||||
@@ -208,5 +254,9 @@ export function renderScene(canvas, state) {
|
||||
drawSide(ctx, tf, state.pose.near, 1, 2.6);
|
||||
}
|
||||
|
||||
if (state.showAnnotations && state.showNear) {
|
||||
drawAnnotations(ctx, tf, state.pose.near);
|
||||
}
|
||||
|
||||
drawHUD(ctx, state.pose);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export function wireUI(state, hooks) {
|
||||
const showNear = document.getElementById('show-near');
|
||||
const showFar = document.getElementById('show-far');
|
||||
const showTrace = document.getElementById('show-trace');
|
||||
const showAnnotations = document.getElementById('show-annotations');
|
||||
|
||||
speed.value = String(state.speedRps);
|
||||
speedOut.value = Number(state.speedRps).toFixed(2);
|
||||
@@ -20,6 +21,7 @@ export function wireUI(state, hooks) {
|
||||
showNear.checked = state.showNear;
|
||||
showFar.checked = state.showFar;
|
||||
showTrace.checked = state.showTrace;
|
||||
showAnnotations.checked = state.showAnnotations;
|
||||
|
||||
speed.addEventListener('input', () => {
|
||||
const v = Number(speed.value);
|
||||
@@ -55,6 +57,10 @@ export function wireUI(state, hooks) {
|
||||
state.showTrace = showTrace.checked;
|
||||
});
|
||||
|
||||
showAnnotations.addEventListener('change', () => {
|
||||
state.showAnnotations = showAnnotations.checked;
|
||||
});
|
||||
|
||||
return {
|
||||
syncAngle(theta) {
|
||||
const deg = radToDeg(theta);
|
||||
|
||||
Reference in New Issue
Block a user