All files / web/src/components/toys/number-line/constants computeConstantVisibility.ts

0% Statements 0/71
0% Branches 0/1
0% Functions 0/1
0% Lines 0/71

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                                                                                                                                               
import type { NumberLineState, RenderConstant } from '../types'
import { numberToScreenX } from '../numberLineTicks'
import type { MathConstant } from './constantsData'

export interface ConstantVisibility {
  constant: MathConstant
  screenX: number
  isOnScreen: boolean
  opacity: number
}

/**
 * Compute visibility for a single constant.
 *
 * A constant is visible when:
 * 1. It's on screen (within canvas bounds + margin)
 * 2. The zoom level is adequate to distinguish it (pxPerPrecisionUnit >= 20)
 *
 * Unlike FindTheNumber, there's no position proximity factor —
 * constants appear whenever on screen AND zoomed enough.
 */
export function computeConstantVisibility(
  constant: MathConstant,
  state: NumberLineState,
  canvasWidth: number
): ConstantVisibility {
  const screenX = numberToScreenX(constant.value, state.center, state.pixelsPerUnit, canvasWidth)

  // Margin so constants don't pop in right at the edge
  const margin = 40
  const isOnScreen = screenX >= -margin && screenX <= canvasWidth + margin

  if (!isOnScreen) {
    return { constant, screenX, isOnScreen, opacity: 0 }
  }

  // Zoom adequacy: one unit at the constant's precision should be >= 20px
  const unitAtPrecision = 10 ** -constant.revealPrecision
  const pxPerPrecisionUnit = unitAtPrecision * state.pixelsPerUnit
  const opacity = Math.min(1, pxPerPrecisionUnit / 20)

  return { constant, screenX, isOnScreen, opacity }
}

/**
 * Compute visibility for all constants at once.
 * Returns only those with opacity > 0.
 */
export function computeAllConstantVisibilities(
  constants: MathConstant[],
  state: NumberLineState,
  canvasWidth: number,
  discoveredSet: Set<string>
): RenderConstant[] {
  const result: RenderConstant[] = []

  for (const constant of constants) {
    const vis = computeConstantVisibility(constant, state, canvasWidth)
    if (vis.opacity > 0) {
      result.push({
        id: constant.id,
        symbol: constant.symbol,
        screenX: vis.screenX,
        opacity: vis.opacity,
        discovered: discoveredSet.has(constant.id),
      })
    }
  }

  return result
}