import { useState , useMemo , useLayoutEffect } from "react" ; import { percentToRange , clamp } from "../../utils" ; import { BANDS } from "../../constants" ; import spline from "./spline" ; import * as Selectors from "../../selectors" ; import { usePromiseValueOrNull , useTypedSelector } from "../../hooks" ; import { Slider } from "../../types" ; const GRAPH_HEIGHT = 19 ; const GRAPH_WIDTH = 113 ; function EqGraph ( ) { const sliders = useTypedSelector ( Selectors . getSliders ) ; const preampLineImagePromise = useTypedSelector ( Selectors . getPreampLineImage ) ; const preampLineImage = usePromiseValueOrNull ( preampLineImagePromise ) ; const [ canvasNode , setCanvasNode ] = useState < HTMLCanvasElement | null > ( null ) ; const canvasCtx = useMemo ( ( ) => canvasNode ?. getContext ( "2d" ) ?? null , [ canvasNode ] ) ; const colorPattern = useColorPattern ( canvasCtx ) ; useLayoutEffect ( ( ) => { if ( canvasCtx == null || canvasNode == null || preampLineImage == null || colorPattern == null ) { return ; } const width = Number ( canvasNode . width ) ; const height = Number ( canvasNode . height ) ; canvasCtx . clearRect ( 0 , 0 , width , height ) ; drawEqLine ( { colorPattern , sliders , canvasCtx , preampLineImage } ) ; } , [ canvasCtx , canvasNode , colorPattern , preampLineImage , sliders ] ) ; return ( < canvas id = "eqGraph" ref = { setCanvasNode } width = { GRAPH_WIDTH } height = { GRAPH_HEIGHT } / > ) ; } function useColorPattern ( canvasCtx ) { const lineColorsImagePromise = useTypedSelector ( Selectors . getLineColorsImage ) ; const lineColorsImage = usePromiseValueOrNull ( lineColorsImagePromise ) ; return useMemo ( ( ) => { if ( canvasCtx == null || lineColorsImage == null ) { return null ; } return canvasCtx . createPattern ( lineColorsImage , "repeat-x" ) ; } , [ canvasCtx , lineColorsImage ] ) ; } function drawEqLine ( { colorPattern , sliders , canvasCtx , preampLineImage , } ) { const preampValue = percentToRange ( sliders . preamp / 100 , 0 , GRAPH_HEIGHT - 1 ) ; canvasCtx . drawImage ( preampLineImage , 0 , preampValue , preampLineImage . width , preampLineImage . height ) ; const amplitudes = BANDS . map ( ( band ) => sliders [ band ] ) ; canvasCtx . fillStyle = colorPattern ; const paddingLeft = 2 ; const min = 0 ; const max = GRAPH_HEIGHT - 1 ; const xs = [ ] ; const ys = [ ] ; amplitudes . forEach ( ( value , i ) => { const percent = ( 100 - value ) / 100 ; xs . push ( i * 12 ) ; ys . push ( percentToRange ( percent , min , max ) ) ; } ) ; const allYs = spline ( xs , ys ) ; const maxX = xs [ xs . length - 1 ] ; let lastY = ys [ 0 ] ; for ( let x = 0 ; x <= maxX ; x ++ ) { const y = clamp ( Math . round ( allYs [ x ] ) , 0 , GRAPH_HEIGHT - 1 ) ; const yTop = Math . min ( y , lastY ) ; const height = 1 + Math . abs ( lastY - y ) ; canvasCtx . fillRect ( paddingLeft + x , yTop , 1 , height ) ; lastY = y ; } } export default EqGraph ;