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
+6
-6
@@ -500,12 +500,12 @@
|
|||||||
<div class="p3-section">
|
<div class="p3-section">
|
||||||
<h3>Posa target</h3>
|
<h3>Posa target</h3>
|
||||||
<div class="p3-readout">
|
<div class="p3-readout">
|
||||||
<div>X <span id="p3X">—</span> mm</div>
|
<label>X <input id="p3X" type="number" step="1" /> mm</label>
|
||||||
<div>Y <span id="p3Y">—</span> mm</div>
|
<label>Y <input id="p3Y" type="number" step="1" /> mm</label>
|
||||||
<div>Z <span id="p3Z">—</span> mm</div>
|
<label>Z <input id="p3Z" type="number" step="1" /> mm</label>
|
||||||
<div>R <span id="p3R">—</span>°</div>
|
<label>R <input id="p3R" type="number" step="1" /> °</label>
|
||||||
<div>P <span id="p3P">—</span>°</div>
|
<label>P <input id="p3P" type="number" step="1" /> °</label>
|
||||||
<div>W <span id="p3W">—</span>°</div>
|
<label>W <input id="p3W" type="number" step="1" /> °</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="p3-status" id="p3Status">— polso fermo</div>
|
<div class="p3-status" id="p3Status">— polso fermo</div>
|
||||||
<div class="p3-row" id="p3EditorActions">
|
<div class="p3-row" id="p3EditorActions">
|
||||||
|
|||||||
+19
-1
@@ -80,7 +80,25 @@
|
|||||||
color: #ddd;
|
color: #ddd;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
#tab-pose-3d .p3-readout span { color: #4da3ff; }
|
#tab-pose-3d .p3-readout label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
#tab-pose-3d .p3-readout input {
|
||||||
|
width: 60px;
|
||||||
|
background: #0d1014;
|
||||||
|
border: 1px solid #2a2f36;
|
||||||
|
color: #4da3ff;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
#tab-pose-3d .p3-readout input:focus {
|
||||||
|
outline: 1px solid #4da3ff;
|
||||||
|
border-color: #4da3ff;
|
||||||
|
}
|
||||||
|
|
||||||
#tab-pose-3d .p3-status { font-size: 12px; color: #888; margin-bottom: 8px; }
|
#tab-pose-3d .p3-status { font-size: 12px; color: #888; margin-bottom: 8px; }
|
||||||
#tab-pose-3d .p3-status.modified { color: #f0ad4e; }
|
#tab-pose-3d .p3-status.modified { color: #f0ad4e; }
|
||||||
|
|||||||
+76
-7
@@ -251,6 +251,21 @@ function quatToRPYdeg(q) {
|
|||||||
return [roll * 180 / Math.PI, pitch * 180 / Math.PI, yaw * 180 / Math.PI];
|
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 -----
|
// ----- state apply to scene -----
|
||||||
function updateRealWrists() {
|
function updateRealWrists() {
|
||||||
for (const side of ['right', 'left']) {
|
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() {
|
function updateReadout() {
|
||||||
const side = state.active_side;
|
const side = state.active_side;
|
||||||
const pose = state.target[side] || state.current[side];
|
const pose = state.target[side] || state.current[side];
|
||||||
if (!pose) {
|
if (!pose) {
|
||||||
$('p3X').textContent = '—';
|
setInputIfNotFocused('p3X', '');
|
||||||
|
setInputIfNotFocused('p3Y', '');
|
||||||
|
setInputIfNotFocused('p3Z', '');
|
||||||
|
setInputIfNotFocused('p3R', '');
|
||||||
|
setInputIfNotFocused('p3P', '');
|
||||||
|
setInputIfNotFocused('p3W', '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const p = pose.position, q = pose.quaternion;
|
const p = pose.position, q = pose.quaternion;
|
||||||
$('p3X').textContent = (p[0] * 1000).toFixed(1);
|
setInputIfNotFocused('p3X', (p[0] * 1000).toFixed(1));
|
||||||
$('p3Y').textContent = (p[1] * 1000).toFixed(1);
|
setInputIfNotFocused('p3Y', (p[1] * 1000).toFixed(1));
|
||||||
$('p3Z').textContent = (p[2] * 1000).toFixed(1);
|
setInputIfNotFocused('p3Z', (p[2] * 1000).toFixed(1));
|
||||||
const rpy = quatToRPYdeg(q);
|
const rpy = quatToRPYdeg(q);
|
||||||
$('p3R').textContent = rpy[0].toFixed(1);
|
setInputIfNotFocused('p3R', rpy[0].toFixed(1));
|
||||||
$('p3P').textContent = rpy[1].toFixed(1);
|
setInputIfNotFocused('p3P', rpy[1].toFixed(1));
|
||||||
$('p3W').textContent = rpy[2].toFixed(1);
|
setInputIfNotFocused('p3W', rpy[2].toFixed(1));
|
||||||
$('p3Status').textContent = state.target[side] ? '● modificata' : '— polso fermo';
|
$('p3Status').textContent = state.target[side] ? '● modificata' : '— polso fermo';
|
||||||
$('p3Status').className = 'p3-status' + (state.target[side] ? ' modified' : '');
|
$('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 -----
|
// ----- side change listener -----
|
||||||
function bindSideControls() {
|
function bindSideControls() {
|
||||||
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
||||||
@@ -690,6 +758,7 @@ function ensureInit() {
|
|||||||
bindJogAutoApply();
|
bindJogAutoApply();
|
||||||
bindPoseButtons();
|
bindPoseButtons();
|
||||||
bindUnlockButton();
|
bindUnlockButton();
|
||||||
|
bindReadoutInputs();
|
||||||
pollState();
|
pollState();
|
||||||
pollTimer = setInterval(pollState, POLL_MS);
|
pollTimer = setInterval(pollState, POLL_MS);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user