Source: view/viz/VizSampleDensity1D.js

/** @module */

import VoterRender1D from './VoterRender1D.js'
import VoterRender2D from './VoterRender2D.js'

/**
 * Show Voters
 * @param {VoterRendererList} voterRendererList
 * @param {screen} screen - draw to the screen
 * @constructor
 */
// eslint-disable-next-line max-len
export default function VizSampleDensity1D(voterRendererList, canDnRendererList, screen, changes, simOptions) {
    const self = this

    // adjustable visual parameters
    const kw = 20
    const ikw = 1 / kw

    // sum and total density
    const { width } = screen
    const sum = Array(width).fill(0)
    let total = 0

    const { dimensions } = simOptions

    // voter renderer factory //
    const VoterRenderer = (dimensions === 1) ? VoterRender1D : VoterRender2D
    voterRendererList.setRenderer((voterShape) => new VoterRenderer(voterShape, screen))
    canDnRendererList.setRenderer((voterShape) => new VoterRenderer(voterShape, screen))

    self.update = function (samplingResult) {
        if (changes.checkAny()) {
            start()
        }

        const { pointsChanged, samplingPointsList, samplingPointsCount } = samplingResult

        if (pointsChanged) {
            updatePoints(samplingPointsList, samplingPointsCount)
        }
    }

    function start() {
        sum.fill(0)
        total = 0
    }

    const kernelsize = 4 / 3
    function updatePoints(samplingPointsList, samplingPointsCount) {
        // add to sum for each point
        const nk = samplingPointsList.length
        for (let k = 0; k < nk; k++) {
            const x = samplingPointsList[k][0]
            const count = samplingPointsCount[k]
            for (let i = 0; i < width; i++) {
                sum[i] += kernel(i - x) * count
            }
            total += kernelsize * count
        }
    }

    function kernel(x) {
        if (x > kw || x < -kw) return 0
        return (1 - (x * ikw) ** 2) * ikw
    }

    self.render = () => {
        renderCans()

        voterRendererList.render()
        canDnRendererList.render()
    }

    function renderCans() {
        const { ctx } = screen
        ctx.save()

        ctx.beginPath()
        doPath(ctx)
        ctx.strokeStyle = '#ccc'
        ctx.stroke()
        ctx.fillStyle = '#ccc'
        ctx.fill()

        ctx.restore()
    }

    const h = 100
    const middle = 150
    function doPath(ctx) {
        const norm = 1 / total

        const amp = h * 100
        const bottom = middle + h * 0.5
        // start bottom left
        ctx.moveTo(0, bottom)
        const pa = []
        for (let i = 0; i <= width; i += 1) {
            const y = bottom - amp * sum[i] * norm
            pa.push(y)
            ctx.lineTo(i, y)
        }
        // end bottom right
        ctx.lineTo(screen.width, bottom)
        ctx.lineTo(0, bottom)
    }
}