All files / owid-grapher/grapher/scatterCharts ComparisonLineGenerator.ts

96.3% Statements 52/54
83.33% Branches 10/12
100% Functions 4/4
96.3% Lines 52/54

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 611x 1x 1x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 3000x 3000x 3000x 3000x 3000x 3000x 2999x 2999x 2999x 6x 6x 6x 1x 3000x 3000x 3000x 3000x 3000x 3000x 3000x 3000x 3000x     3000x 1x 6x 6x              
import { Parser, Expression } from "expr-eval"
import { scaleLinear, scaleLog } from "d3-scale"
import { ScaleType } from "../core/GrapherConstants"
 
export function generateComparisonLinePoints(
    lineFunction: string = "x",
    xScaleDomain: [number, number],
    yScaleDomain: [number, number],
    xScaleType: ScaleType,
    yScaleType: ScaleType
): [number, number][] {
    const expr = parseEquation(lineFunction)?.simplify({
        e: Math.E,
        pi: Math.PI,
    })
    const yFunc = (x: number): number | undefined =>
        evalExpression(expr, { x }, undefined)
 
    // Construct control data by running the equation across sample points
    const numPoints = 500
 
    const scaleFunction = xScaleType === ScaleType.log ? scaleLog : scaleLinear
    const scale = scaleFunction().domain(xScaleDomain).range([0, numPoints])
 
    const controlData: Array<[number, number]> = []
    for (let i = 0; i < numPoints; i++) {
        const x = scale.invert(i)
        const y = yFunc(x)
 
        if (y === undefined || Number.isNaN(x) || Number.isNaN(y)) continue
        if (xScaleType === ScaleType.log && x <= 0) continue
        if (yScaleType === ScaleType.log && y <= 0) continue
        if (y > yScaleDomain[1]) continue
        controlData.push([x, y])
    }
 
    return controlData
}
 
function evalExpression<D>(
    expr: Expression | undefined,
    context: Record<string, number>,
    defaultOnError: D
): number | D {
    if (expr === undefined) return defaultOnError
    try {
        return expr.evaluate(context) as number
    } catch (e) {
        return defaultOnError
    }
}
 
function parseEquation(equation: string): Expression | undefined {
    try {
        return Parser.parse(equation)
    } catch (e) {
        console.error(e)
        return undefined
    }
}