Source: view/viz/Grid1D.js

/** @module */

import colorBlender, { rgbToString } from './colorBlender.js'
import getTallyFractionsNameForVote from './getTallyFractionsNameForVote.js'

/**
 * Draw grid cells to show votes.
 * @param {VoterShape} voterGeom
 * @param {Screen} screen
 * @constructor
 */
export default function Grid1D(candidateList, screen) {
    const self = this

    const h = 200
    const center = 100

    let votesForGeom
    let canList
    self.update = (votesForGeom0) => {
        votesForGeom = votesForGeom0
        canList = candidateList.getEntities()
    }

    self.renderBackground = function () {
        const { ctx } = screen
        ctx.save()
        ctx.globalAlpha = 0.7

        // draw each can separately
        const nCans = canList.length
        for (let i = 0; i < nCans; i++) {
            ctx.strokeStyle = '#dddddd'
            // ctx.strokeStyle = '#333333'
            ctx.beginPath()
            shapePath(ctx, i, true)
            ctx.stroke()
        }
        ctx.restore()
    }
    self.render = function () {
        const { ctx } = screen
        ctx.save()
        ctx.globalAlpha = 0.7
        const nCans = canList.length
        for (let i = 0; i < nCans; i++) {
            // draw image
            ctx.fillStyle = canList[i].color
            const { colorRGBA } = canList[i]
            ctx.strokeStyle = rgbToString(colorBlender([0.5, 0.5], [colorRGBA, [0, 0, 0]]))
            ctx.beginPath()
            shapePath(ctx, i, false)
            ctx.fill()
            ctx.stroke()
        }
        ctx.restore()
    }
    function shapePath(ctx, iCan, drawOutline) {
        const { grid, voteSet } = votesForGeom
        const { x, w, densityProfile } = grid.voterGeom
        const nCans = canList.length

        const isGauss = (densityProfile === 'gaussian')
        const gridX = grid.x
        const sigma = w / Math.sqrt(2 * Math.PI) // w = sigma * sqrt(2*pi)
        const amp = h / nCans
        const bottom = center + h * 0.5 - iCan * amp
        // start bottom left
        // go outside of screen by one pixel
        const left = Math.max(-1, gridX[0])
        ctx.moveTo(left, bottom)
        const gl = gridX.length
        const tallyName = getTallyFractionsNameForVote(voteSet[0])
        for (let i = 0; i < gl; i += 1) {
            const xg = gridX[i]
            if (xg < -1) continue
            if (xg > screen.width) continue
            const voteMult = (drawOutline) ? 1 : voteSet[i][tallyName][iCan]
            const shapeMult = (isGauss) ? Math.exp(-0.5 * ((xg - x) / sigma) ** 2) : 1
            const y = bottom - amp * shapeMult * voteMult
            ctx.lineTo(xg, y)
        }
        // end bottom right
        const right = Math.min(screen.width, gridX[gl - 1])
        ctx.lineTo(right, bottom)
        // close path
        ctx.lineTo(left, bottom)
    }
}