feat(pose_3d): selezione polso + gizmo translate/rotate sul ghost
This commit is contained in:
1 parent
7a561cfa67
commit
64500944d6
1 file changed
+118
+118
@@ -197,6 +197,27 @@ function updateRealWrists() {
|
||||
}
|
||||
}
|
||||
|
||||
function syncGhostsToTargetOrReal() {
|
||||
for (const side of ['right', 'left']) {
|
||||
const ghost = state.ghosts[side];
|
||||
if (!ghost) continue;
|
||||
if (state.target[side]) {
|
||||
// ghost segue il target locale (mosso dal gizmo)
|
||||
ghost.visible = true;
|
||||
} else if (side === state.active_side) {
|
||||
// ghost allineato al reale (preparato per drag)
|
||||
const real = state.current[side];
|
||||
if (real) {
|
||||
ghost.position.set(real.position[0], real.position[1], real.position[2]);
|
||||
ghost.quaternion.set(real.quaternion[0], real.quaternion[1],
|
||||
real.quaternion[2], real.quaternion[3]);
|
||||
}
|
||||
} else {
|
||||
ghost.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateReadout() {
|
||||
const side = state.active_side;
|
||||
const pose = state.target[side] || state.current[side];
|
||||
@@ -226,6 +247,7 @@ async function pollState() {
|
||||
state.saved_poses = s.poses || {};
|
||||
state.live_max_step_m = s.live_max_step_m || 0.15;
|
||||
updateRealWrists();
|
||||
syncGhostsToTargetOrReal();
|
||||
updateReadout();
|
||||
// Aggiorna dropdown pose salvate
|
||||
refreshPoseSelect();
|
||||
@@ -248,6 +270,99 @@ function refreshPoseSelect() {
|
||||
sel.value = cur;
|
||||
}
|
||||
|
||||
// ----- selezione polso via click -----
|
||||
function setupSelection() {
|
||||
const { renderer, camera, transform } = state.three;
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const mouse = new THREE.Vector2();
|
||||
const canvas = renderer.domElement;
|
||||
|
||||
canvas.addEventListener('pointerdown', (e) => {
|
||||
// Se sta interagendo con il gizmo, non fare nulla
|
||||
if (transform.dragging) return;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
// Verifica intersezione con sphere destra o sinistra
|
||||
const candidates = [state.arms.right.userData.sphere,
|
||||
state.arms.left.userData.sphere];
|
||||
const hits = raycaster.intersectObjects(candidates, false);
|
||||
if (hits.length > 0) {
|
||||
const sphere = hits[0].object;
|
||||
const side = sphere.parent.userData.side;
|
||||
activateSide(side);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function activateSide(side) {
|
||||
state.active_side = side;
|
||||
// sync radio buttons
|
||||
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
||||
r.checked = (r.value === side);
|
||||
}
|
||||
// Attacca gizmo al ghost (lo creiamo se non esiste, posizionandolo sul reale)
|
||||
ensureGhostInit(side);
|
||||
const { transform } = state.three;
|
||||
transform.attach(state.ghosts[side]);
|
||||
state.ghosts[side].visible = true;
|
||||
updateReadout();
|
||||
}
|
||||
|
||||
function ensureGhostInit(side) {
|
||||
// Se non c'è target locale, allinea ghost al reale (preparato per drag)
|
||||
if (!state.target[side]) {
|
||||
const real = state.current[side];
|
||||
if (real) {
|
||||
state.ghosts[side].position.set(real.position[0], real.position[1], real.position[2]);
|
||||
state.ghosts[side].quaternion.set(real.quaternion[0], real.quaternion[1],
|
||||
real.quaternion[2], real.quaternion[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----- gizmo mode toggle -----
|
||||
function bindGizmoControls() {
|
||||
for (const r of document.querySelectorAll('input[name=p3Gizmo]')) {
|
||||
r.addEventListener('change', () => {
|
||||
state.gizmo_mode = r.value;
|
||||
state.three.transform.setMode(r.value);
|
||||
});
|
||||
}
|
||||
// tasti t/r
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (!$('tab-pose-3d')?.classList.contains('active')) return;
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||
if (e.key === 't') { setGizmoMode('translate'); }
|
||||
if (e.key === 'r') { setGizmoMode('rotate'); }
|
||||
});
|
||||
}
|
||||
|
||||
function setGizmoMode(mode) {
|
||||
state.gizmo_mode = mode;
|
||||
state.three.transform.setMode(mode);
|
||||
for (const r of document.querySelectorAll('input[name=p3Gizmo]')) {
|
||||
r.checked = (r.value === mode);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- target tracking durante drag gizmo -----
|
||||
function bindGizmoEvents() {
|
||||
const { transform } = state.three;
|
||||
transform.addEventListener('change', () => {
|
||||
const side = state.active_side;
|
||||
const ghost = state.ghosts[side];
|
||||
if (!ghost) return;
|
||||
state.target[side] = {
|
||||
position: [ghost.position.x, ghost.position.y, ghost.position.z],
|
||||
quaternion: [ghost.quaternion.x, ghost.quaternion.y,
|
||||
ghost.quaternion.z, ghost.quaternion.w],
|
||||
};
|
||||
updateReadout();
|
||||
});
|
||||
}
|
||||
|
||||
// ----- side change listener -----
|
||||
function bindSideControls() {
|
||||
for (const r of document.querySelectorAll('input[name=p3Side]')) {
|
||||
@@ -265,7 +380,10 @@ function ensureInit() {
|
||||
if (!$('p3Canvas')) return;
|
||||
inited = true;
|
||||
initScene();
|
||||
setupSelection();
|
||||
bindSideControls();
|
||||
bindGizmoControls();
|
||||
bindGizmoEvents();
|
||||
pollState();
|
||||
pollTimer = setInterval(pollState, POLL_MS);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user