feat(pose_3d): readout XYZ/RPY editabili (input number, drive il ghost)
This commit is contained in:
1 parent
8b722ca729
commit
b852976a4a
3 files changed
+101
-14
No files matched your search
+76
-7
@@ -251,6 +251,21 @@ function quatToRPYdeg(q) {
|
||||
return [roll * 180 / Math.PI, pitch * 180 / Math.PI, yaw * 180 / Math.PI];
|
||||
}
|
||||
|
||||
function rpyDegToQuat(roll, pitch, yaw) {
|
||||
const r = roll * Math.PI / 180;
|
||||
const p = pitch * Math.PI / 180;
|
||||
const y = yaw * Math.PI / 180;
|
||||
const cr = Math.cos(r / 2), sr = Math.sin(r / 2);
|
||||
const cp = Math.cos(p / 2), sp = Math.sin(p / 2);
|
||||
const cy = Math.cos(y / 2), sy = Math.sin(y / 2);
|
||||
// ZYX intrinsic (matches quatToRPYdeg)
|
||||
const qx = sr * cp * cy - cr * sp * sy;
|
||||
const qy = cr * sp * cy + sr * cp * sy;
|
||||
const qz = cr * cp * sy - sr * sp * cy;
|
||||
const qw = cr * cp * cy + sr * sp * sy;
|
||||
return [qx, qy, qz, qw];
|
||||
}
|
||||
|
||||
// ----- state apply to scene -----
|
||||
function updateRealWrists() {
|
||||
for (const side of ['right', 'left']) {
|
||||
@@ -294,21 +309,33 @@ function syncGhostsToTargetOrReal() {
|
||||
}
|
||||
}
|
||||
|
||||
function setInputIfNotFocused(id, value) {
|
||||
const el = $(id);
|
||||
if (!el) return;
|
||||
if (document.activeElement === el) return; // user is typing, don't stomp
|
||||
el.value = value;
|
||||
}
|
||||
|
||||
function updateReadout() {
|
||||
const side = state.active_side;
|
||||
const pose = state.target[side] || state.current[side];
|
||||
if (!pose) {
|
||||
$('p3X').textContent = '—';
|
||||
setInputIfNotFocused('p3X', '');
|
||||
setInputIfNotFocused('p3Y', '');
|
||||
setInputIfNotFocused('p3Z', '');
|
||||
setInputIfNotFocused('p3R', '');
|
||||
setInputIfNotFocused('p3P', '');
|
||||
setInputIfNotFocused('p3W', '');
|
||||
return;
|
||||
}
|
||||
const p = pose.position, q = pose.quaternion;
|
||||
$('p3X').textContent = (p[0] * 1000).toFixed(1);
|
||||
$('p3Y').textContent = (p[1] * 1000).toFixed(1);
|
||||
$('p3Z').textContent = (p[2] * 1000).toFixed(1);
|
||||
setInputIfNotFocused('p3X', (p[0] * 1000).toFixed(1));
|
||||
setInputIfNotFocused('p3Y', (p[1] * 1000).toFixed(1));
|
||||
setInputIfNotFocused('p3Z', (p[2] * 1000).toFixed(1));
|
||||
const rpy = quatToRPYdeg(q);
|
||||
$('p3R').textContent = rpy[0].toFixed(1);
|
||||
$('p3P').textContent = rpy[1].toFixed(1);
|
||||
$('p3W').textContent = rpy[2].toFixed(1);
|
||||
setInputIfNotFocused('p3R', rpy[0].toFixed(1));
|
||||
setInputIfNotFocused('p3P', rpy[1].toFixed(1));
|
||||
setInputIfNotFocused('p3W', rpy[2].toFixed(1));
|
||||
$('p3Status').textContent = state.target[side] ? '● modificata' : '— polso fermo';
|
||||
$('p3Status').className = 'p3-status' + (state.target[side] ? ' modified' : '');
|
||||
}
|
||||
@@ -447,6 +474,47 @@ function bindGizmoEvents() {
|
||||
});
|
||||
}
|
||||
|
||||
function readReadoutInputs() {
|
||||
const num = (id, dflt) => {
|
||||
const v = parseFloat($(id).value);
|
||||
return Number.isFinite(v) ? v : dflt;
|
||||
};
|
||||
const side = state.active_side;
|
||||
if (!side) return;
|
||||
// fallback values: current pose if any, else SHOULDER position via arm
|
||||
const fallback = state.current[side] || (state.arms[side] ? {
|
||||
position: [state.arms[side].position.x, state.arms[side].position.y, state.arms[side].position.z],
|
||||
quaternion: [0, 0, 0, 1],
|
||||
} : { position: [0, 0, 0], quaternion: [0, 0, 0, 1] });
|
||||
const x_mm = num('p3X', fallback.position[0] * 1000);
|
||||
const y_mm = num('p3Y', fallback.position[1] * 1000);
|
||||
const z_mm = num('p3Z', fallback.position[2] * 1000);
|
||||
const r_deg = num('p3R', 0);
|
||||
const p_deg = num('p3P', 0);
|
||||
const w_deg = num('p3W', 0);
|
||||
const position = [x_mm / 1000, y_mm / 1000, z_mm / 1000];
|
||||
const quaternion = rpyDegToQuat(r_deg, p_deg, w_deg);
|
||||
state.target[side] = { position, quaternion };
|
||||
// Aggiorna ghost in scena
|
||||
const ghost = state.ghosts[side];
|
||||
if (ghost) {
|
||||
ghost.position.set(position[0], position[1], position[2]);
|
||||
ghost.quaternion.set(quaternion[0], quaternion[1], quaternion[2], quaternion[3]);
|
||||
ghost.visible = true;
|
||||
}
|
||||
// Status
|
||||
$('p3Status').textContent = '● modificata (da input)';
|
||||
$('p3Status').className = 'p3-status modified';
|
||||
}
|
||||
|
||||
function bindReadoutInputs() {
|
||||
for (const id of ['p3X', 'p3Y', 'p3Z', 'p3R', 'p3P', 'p3W']) {
|
||||
const el = $(id);
|
||||
if (!el) continue;
|
||||
el.addEventListener('change', readReadoutInputs); // fire on blur / Enter
|
||||
}
|
||||
}
|
||||
|
||||
// ----- side change listener -----
|
||||
function bindSideControls() {
|
||||
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
||||
@@ -690,6 +758,7 @@ function ensureInit() {
|
||||
bindJogAutoApply();
|
||||
bindPoseButtons();
|
||||
bindUnlockButton();
|
||||
bindReadoutInputs();
|
||||
pollState();
|
||||
pollTimer = setInterval(pollState, POLL_MS);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user