Update viewer_ar.js
Browse files- viewer_ar.js +58 -38
viewer_ar.js
CHANGED
|
@@ -278,68 +278,81 @@
|
|
| 278 |
});
|
| 279 |
});
|
| 280 |
|
| 281 |
-
//
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
let
|
|
|
|
|
|
|
| 285 |
let lastMouseX = 0;
|
| 286 |
|
| 287 |
-
//
|
| 288 |
-
|
| 289 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
const RAD2DEG = 180 / Math.PI;
|
| 291 |
|
| 292 |
-
// Déplacement (input XR
|
| 293 |
app.xr.input.on("add", (inputSource) => {
|
| 294 |
inputSource.on("selectstart", () => {
|
| 295 |
-
if (!placedOnce || !modelLoaded || multiTouchActive) return;
|
| 296 |
inputSource.hitTestStart({
|
| 297 |
entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
|
| 298 |
callback: (err, transientSource) => {
|
| 299 |
if (err) return;
|
| 300 |
isDragging = true;
|
|
|
|
| 301 |
|
| 302 |
transientSource.on("result", (pos /*, rot */) => {
|
| 303 |
-
|
|
|
|
| 304 |
modelRoot.setPosition(pos);
|
| 305 |
});
|
| 306 |
|
| 307 |
transientSource.once("remove", () => {
|
| 308 |
-
|
|
|
|
| 309 |
});
|
| 310 |
}
|
| 311 |
});
|
| 312 |
});
|
| 313 |
|
| 314 |
inputSource.on("selectend", () => {
|
| 315 |
-
|
|
|
|
| 316 |
});
|
| 317 |
});
|
| 318 |
|
| 319 |
-
// =====
|
| 320 |
if (app.touch) {
|
| 321 |
const touches = new Map(); // id -> {x,y}
|
| 322 |
|
| 323 |
-
const
|
| 324 |
if (touches.size < 2) return null;
|
| 325 |
const it = touches.values();
|
| 326 |
-
const
|
| 327 |
-
const
|
| 328 |
-
return Math.atan2(
|
| 329 |
};
|
| 330 |
|
| 331 |
app.touch.on("touchstart", (e) => {
|
| 332 |
for (const t of e.touches) touches.set(t.id, { x: t.x, y: t.y });
|
| 333 |
|
| 334 |
if (touches.size >= 2) {
|
| 335 |
-
multiTouchActive = true;
|
| 336 |
-
rotateMode = true;
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
rotationBaseY = modelRoot.getEulerAngles().y; // point de départ
|
| 342 |
-
}
|
| 343 |
}
|
| 344 |
});
|
| 345 |
|
|
@@ -348,29 +361,32 @@
|
|
| 348 |
if (touches.has(t.id)) touches.set(t.id, { x: t.x, y: t.y });
|
| 349 |
}
|
| 350 |
|
|
|
|
| 351 |
if (rotateMode && multiTouchActive && modelRoot.enabled && touches.size >= 2) {
|
| 352 |
-
const ang =
|
| 353 |
-
if (ang !== null) {
|
| 354 |
-
|
| 355 |
-
//
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 360 |
}
|
| 361 |
}
|
| 362 |
});
|
| 363 |
|
| 364 |
app.touch.on("touchend", (e) => {
|
| 365 |
for (const t of e.changedTouches) touches.delete(t.id);
|
|
|
|
| 366 |
if (touches.size < 2) {
|
| 367 |
-
// fin du geste 2 doigts
|
| 368 |
-
if (rotateMode && multiTouchActive) {
|
| 369 |
-
// fige la base à la rotation actuelle pour le prochain geste
|
| 370 |
-
rotationBaseY = modelRoot.getEulerAngles().y;
|
| 371 |
-
}
|
| 372 |
rotateMode = false;
|
| 373 |
multiTouchActive = false;
|
|
|
|
| 374 |
}
|
| 375 |
});
|
| 376 |
|
|
@@ -378,6 +394,8 @@
|
|
| 378 |
touches.clear();
|
| 379 |
rotateMode = false;
|
| 380 |
multiTouchActive = false;
|
|
|
|
|
|
|
| 381 |
});
|
| 382 |
}
|
| 383 |
|
|
@@ -428,6 +446,8 @@
|
|
| 428 |
isDragging = false;
|
| 429 |
rotateMode = false;
|
| 430 |
multiTouchActive = false;
|
|
|
|
|
|
|
| 431 |
});
|
| 432 |
|
| 433 |
app.xr.on(`available:${pc.XRTYPE_AR}`, (available) => {
|
|
|
|
| 278 |
});
|
| 279 |
});
|
| 280 |
|
| 281 |
+
// =========================
|
| 282 |
+
// --- Interactions (MODIF)
|
| 283 |
+
// =========================
|
| 284 |
+
let isDragging = false; // déplacement du modèle
|
| 285 |
+
let rotateMode = false; // rotation 2 doigts (tactile) ou souris
|
| 286 |
+
let multiTouchActive = false; // >= 2 doigts
|
| 287 |
let lastMouseX = 0;
|
| 288 |
|
| 289 |
+
// Nouveaux états pour gérer/annuler les hit-tests transitoires démarrés par le 1er doigt
|
| 290 |
+
const activeTransientSources = new Set(); // XRHitTestSource
|
| 291 |
+
function cancelAllTransientHitTests() {
|
| 292 |
+
activeTransientSources.forEach(src => {
|
| 293 |
+
try { src.remove && src.remove(); } catch(_) {}
|
| 294 |
+
});
|
| 295 |
+
activeTransientSources.clear();
|
| 296 |
+
isDragging = false;
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
// ===== Rotation 2 doigts (incrémentale) =====
|
| 300 |
+
let lastTwoFingerAngle = null; // radians
|
| 301 |
const RAD2DEG = 180 / Math.PI;
|
| 302 |
|
| 303 |
+
// Déplacement (input XR) — ne démarre que si pas de 2 doigts
|
| 304 |
app.xr.input.on("add", (inputSource) => {
|
| 305 |
inputSource.on("selectstart", () => {
|
| 306 |
+
if (!placedOnce || !modelLoaded || multiTouchActive) return;
|
| 307 |
inputSource.hitTestStart({
|
| 308 |
entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
|
| 309 |
callback: (err, transientSource) => {
|
| 310 |
if (err) return;
|
| 311 |
isDragging = true;
|
| 312 |
+
activeTransientSources.add(transientSource);
|
| 313 |
|
| 314 |
transientSource.on("result", (pos /*, rot */) => {
|
| 315 |
+
// Pas de translation si rotation 2 doigts ou multitouch
|
| 316 |
+
if (!isDragging || rotateMode || multiTouchActive) return;
|
| 317 |
modelRoot.setPosition(pos);
|
| 318 |
});
|
| 319 |
|
| 320 |
transientSource.once("remove", () => {
|
| 321 |
+
activeTransientSources.delete(transientSource);
|
| 322 |
+
if (activeTransientSources.size === 0) isDragging = false;
|
| 323 |
});
|
| 324 |
}
|
| 325 |
});
|
| 326 |
});
|
| 327 |
|
| 328 |
inputSource.on("selectend", () => {
|
| 329 |
+
// Libère seulement si plus de sources actives
|
| 330 |
+
cancelAllTransientHitTests();
|
| 331 |
});
|
| 332 |
});
|
| 333 |
|
| 334 |
+
// ===== Gestes tactiles =====
|
| 335 |
if (app.touch) {
|
| 336 |
const touches = new Map(); // id -> {x,y}
|
| 337 |
|
| 338 |
+
const twoFingerAngle = () => {
|
| 339 |
if (touches.size < 2) return null;
|
| 340 |
const it = touches.values();
|
| 341 |
+
const a = it.next().value;
|
| 342 |
+
const b = it.next().value;
|
| 343 |
+
return Math.atan2(b.y - a.y, b.x - a.x);
|
| 344 |
};
|
| 345 |
|
| 346 |
app.touch.on("touchstart", (e) => {
|
| 347 |
for (const t of e.touches) touches.set(t.id, { x: t.x, y: t.y });
|
| 348 |
|
| 349 |
if (touches.size >= 2) {
|
| 350 |
+
multiTouchActive = true;
|
| 351 |
+
rotateMode = true;
|
| 352 |
+
// cancel toute translation issue d'un 1er doigt déjà actif
|
| 353 |
+
cancelAllTransientHitTests();
|
| 354 |
+
// initialise l'angle de référence
|
| 355 |
+
lastTwoFingerAngle = twoFingerAngle();
|
|
|
|
|
|
|
| 356 |
}
|
| 357 |
});
|
| 358 |
|
|
|
|
| 361 |
if (touches.has(t.id)) touches.set(t.id, { x: t.x, y: t.y });
|
| 362 |
}
|
| 363 |
|
| 364 |
+
// Rotation à 2 doigts : appliquer delta d'angle incrémental
|
| 365 |
if (rotateMode && multiTouchActive && modelRoot.enabled && touches.size >= 2) {
|
| 366 |
+
const ang = twoFingerAngle();
|
| 367 |
+
if (ang !== null && lastTwoFingerAngle !== null) {
|
| 368 |
+
let delta = ang - lastTwoFingerAngle; // radians
|
| 369 |
+
// normaliser (-PI, PI) pour éviter les sauts
|
| 370 |
+
if (delta > Math.PI) delta -= 2 * Math.PI;
|
| 371 |
+
if (delta < -Math.PI) delta += 2 * Math.PI;
|
| 372 |
+
|
| 373 |
+
const eul = modelRoot.getEulerAngles();
|
| 374 |
+
modelRoot.setEulerAngles(eul.x, eul.y + delta * RAD2DEG, eul.z);
|
| 375 |
+
|
| 376 |
+
// mise à jour incrémentale
|
| 377 |
+
lastTwoFingerAngle = ang;
|
| 378 |
}
|
| 379 |
}
|
| 380 |
});
|
| 381 |
|
| 382 |
app.touch.on("touchend", (e) => {
|
| 383 |
for (const t of e.changedTouches) touches.delete(t.id);
|
| 384 |
+
|
| 385 |
if (touches.size < 2) {
|
| 386 |
+
// fin du geste 2 doigts : sortir proprement du mode rotation
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
rotateMode = false;
|
| 388 |
multiTouchActive = false;
|
| 389 |
+
lastTwoFingerAngle = null;
|
| 390 |
}
|
| 391 |
});
|
| 392 |
|
|
|
|
| 394 |
touches.clear();
|
| 395 |
rotateMode = false;
|
| 396 |
multiTouchActive = false;
|
| 397 |
+
lastTwoFingerAngle = null;
|
| 398 |
+
cancelAllTransientHitTests();
|
| 399 |
});
|
| 400 |
}
|
| 401 |
|
|
|
|
| 446 |
isDragging = false;
|
| 447 |
rotateMode = false;
|
| 448 |
multiTouchActive = false;
|
| 449 |
+
lastTwoFingerAngle = null;
|
| 450 |
+
cancelAllTransientHitTests();
|
| 451 |
});
|
| 452 |
|
| 453 |
app.xr.on(`available:${pc.XRTYPE_AR}`, (available) => {
|