All files / web/src/arcade-games/know-your-world/utils zoomCapping.ts

100% Statements 122/122
100% Branches 8/8
100% Functions 2/2
100% Lines 122/122

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1231x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 15x 15x 15x 15x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 15x 2x 2x 2x 2x 2x 2x 2x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 2x 2x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x  
/**
 * Zoom Capping Logic
 *
 * This module contains functions for capping zoom levels at the precision
 * mode threshold. When not in pointer lock mode, zoom is capped to prevent
 * excessive screen pixel ratios that make the magnifier too sensitive.
 */
 
import {
  calculateScreenPixelRatio,
  calculateMaxZoomAtThreshold,
  isAboveThreshold,
  type ZoomContext,
} from './screenPixelRatio'
 
export interface ZoomCappingContext {
  /** The zoom level to potentially cap */
  zoom: number
  /** Width of the magnifier in screen pixels */
  magnifierWidth: number
  /** Width of the SVG viewBox */
  viewBoxWidth: number
  /** Width of the SVG element in screen pixels */
  svgWidth: number
  /** Precision mode threshold (e.g., 20 px/px) */
  threshold: number
  /** Whether pointer lock is active (no capping if true) */
  pointerLocked: boolean
}
 
export interface ZoomCappingResult {
  /** The (possibly capped) zoom level */
  cappedZoom: number
  /** Whether the zoom was capped */
  wasCapped: boolean
  /** The original uncapped zoom */
  originalZoom: number
  /** The screen pixel ratio at the original zoom */
  screenPixelRatio: number
}
 
/**
 * Cap zoom at the precision mode threshold if not in pointer lock mode.
 *
 * When not in pointer lock mode, zoom is capped to prevent excessive
 * screen pixel ratios. This ensures the magnifier doesn't become too
 * sensitive before the user activates precision controls.
 *
 * @param context - The zoom capping context
 * @returns The capping result with capped zoom and metadata
 */
export function capZoomAtThreshold(context: ZoomCappingContext): ZoomCappingResult {
  const { zoom, magnifierWidth, viewBoxWidth, svgWidth, threshold, pointerLocked } = context
 
  // No capping when pointer lock is active
  if (pointerLocked) {
    return {
      cappedZoom: zoom,
      wasCapped: false,
      originalZoom: zoom,
      screenPixelRatio: calculateScreenPixelRatio({
        magnifierWidth,
        viewBoxWidth,
        svgWidth,
        zoom,
      }),
    }
  }
 
  // Calculate screen pixel ratio at this zoom
  const screenPixelRatio = calculateScreenPixelRatio({
    magnifierWidth,
    viewBoxWidth,
    svgWidth,
    zoom,
  })
 
  // If below threshold, no capping needed
  if (!isAboveThreshold(screenPixelRatio, threshold)) {
    return {
      cappedZoom: zoom,
      wasCapped: false,
      originalZoom: zoom,
      screenPixelRatio,
    }
  }
 
  // Calculate the maximum zoom at the threshold
  const maxZoom = calculateMaxZoomAtThreshold(threshold, magnifierWidth, svgWidth)
  const cappedZoom = Math.min(zoom, maxZoom)
 
  return {
    cappedZoom,
    wasCapped: true,
    originalZoom: zoom,
    screenPixelRatio,
  }
}
 
/**
 * Check if zoom would be capped at the threshold.
 *
 * This is a simpler check that doesn't return the capped value,
 * useful for determining if precision mode should be recommended.
 *
 * @param context - The zoom capping context
 * @returns True if zoom would be capped
 */
export function wouldZoomBeCapped(context: ZoomCappingContext): boolean {
  if (context.pointerLocked) {
    return false
  }
 
  const screenPixelRatio = calculateScreenPixelRatio({
    magnifierWidth: context.magnifierWidth,
    viewBoxWidth: context.viewBoxWidth,
    svgWidth: context.svgWidth,
    zoom: context.zoom,
  })
 
  return isAboveThreshold(screenPixelRatio, context.threshold)
}