All files / web/src/components/toys/shared collisionDetection.ts

100% Statements 50/50
100% Branches 10/10
100% Functions 1/1
100% Lines 50/50

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 511x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 22x 22x 22x 22x 22x 22x 22x 22x 10x 10x 22x 5x 5x 5x 5x 5x 5x 5x 5x 22x 22x 22x 22x 22x 22x 22x 22x  
/**
 * Label collision detection and fade logic shared between number line and coordinate plane.
 */
 
/** Per-label fade state for smooth collision show/hide transitions */
export interface CollisionFadeEntry {
  /** Whether the label is currently visible (not collision-hidden) */
  visible: boolean
  /** performance.now() when visibility last changed */
  startTime: number
  /** Opacity at the moment visibility changed (to animate from) */
  startOpacity: number
}
 
/** Map from tick value -> fade state, persisted across frames */
export type CollisionFadeMap = Map<number, CollisionFadeEntry>
 
/**
 * Compute collision opacity for a single label, updating the fade map entry.
 *
 * Returns the current opacity (0-1) and whether the animation is still in progress.
 */
export function computeCollisionOpacity(
  value: number,
  isVisible: boolean,
  fadeMap: CollisionFadeMap,
  now: number,
  fadeDurationMs: number
): { opacity: number; animating: boolean } {
  let entry = fadeMap.get(value)
  if (!entry) {
    entry = { visible: isVisible, startTime: now, startOpacity: isVisible ? 1 : 0 }
    fadeMap.set(value, entry)
  } else if (entry.visible !== isVisible) {
    const elapsed = now - entry.startTime
    const t = Math.min(1, elapsed / fadeDurationMs)
    const prevTarget = entry.visible ? 1 : 0
    const currentOpacity = entry.startOpacity + (prevTarget - entry.startOpacity) * t
    entry.visible = isVisible
    entry.startTime = now
    entry.startOpacity = currentOpacity
  }
 
  const elapsed = now - entry.startTime
  const t = Math.min(1, elapsed / fadeDurationMs)
  const target = entry.visible ? 1 : 0
  const opacity = entry.startOpacity + (target - entry.startOpacity) * t
 
  return { opacity, animating: t < 1 }
}