(
canvas: HTMLCanvasElement,
signal: AbortSignal,
)
| 2471 | } |
| 2472 | |
| 2473 | private setupInteraction( |
| 2474 | canvas: HTMLCanvasElement, |
| 2475 | signal: AbortSignal, |
| 2476 | ): void { |
| 2477 | // Wheel zoom — deltaY-proportional for smooth, magnitude-aware zooming |
| 2478 | canvas.addEventListener( |
| 2479 | 'wheel', |
| 2480 | (e) => { |
| 2481 | e.preventDefault(); |
| 2482 | const rect = canvas.getBoundingClientRect(); |
| 2483 | const mouseX = e.clientX - rect.left; |
| 2484 | const mouseY = e.clientY - rect.top; |
| 2485 | |
| 2486 | // Smooth zoom: larger deltaY → bigger zoom step |
| 2487 | const zoomFactor = Math.pow(0.999, e.deltaY); |
| 2488 | const newScale = this.vp.scale * zoomFactor; |
| 2489 | // Adjust position so world point under cursor stays fixed |
| 2490 | this.vp.x = mouseX - (mouseX - this.vp.x) * (newScale / this.vp.scale); |
| 2491 | this.vp.y = mouseY - (mouseY - this.vp.y) * (newScale / this.vp.scale); |
| 2492 | this.vp.scale = newScale; |
| 2493 | |
| 2494 | // User took manual control — stop auto-fitting during indexing. |
| 2495 | this.hasUserMovedCamera = true; |
| 2496 | |
| 2497 | // Hide edges during zoom for instant response (Grafana pattern). |
| 2498 | // Sprite transforms are instant; edges redraw after 1300ms settle. |
| 2499 | if (!this.dragNode) { |
| 2500 | this.hideEdgesForInteraction(); |
| 2501 | } |
| 2502 | // Re-cull labels after zoom settles |
| 2503 | this.debouncedLabelCull(); |
| 2504 | }, |
| 2505 | { passive: false, signal }, |
| 2506 | ); |
| 2507 | |
| 2508 | // Pointer events for pan / drag / click |
| 2509 | let pointerDown = false; |
| 2510 | let pointerDownButton = 0; |
| 2511 | let movedDistance = 0; |
| 2512 | // Track last pointer position to compute pan deltas manually, |
| 2513 | // avoiding movementX/Y which includes the full distance since pointerdown |
| 2514 | // on the first pointermove event after crossing the drag threshold. |
| 2515 | let lastPointerX = 0; |
| 2516 | let lastPointerY = 0; |
| 2517 | |
| 2518 | // Suppress the browser's right-click menu so right-drag can pan in 3D mode. |
| 2519 | canvas.addEventListener( |
| 2520 | 'contextmenu', |
| 2521 | (e) => { |
| 2522 | e.preventDefault(); |
| 2523 | }, |
| 2524 | { signal }, |
| 2525 | ); |
| 2526 | |
| 2527 | canvas.addEventListener( |
| 2528 | 'pointerdown', |
| 2529 | (e) => { |
| 2530 | pointerDown = true; |
no test coverage detected