Simple Arcade Live Chart

Hi Everyone,

I thought I’d share a simple line graph piece of code that will live update as values come in!

class Point {
    x: number;
    y: number;
    constructor(x:number, y: number) {
        this.x = x;
        this.y = y;
    }
}

class Graph {
    X_PADDING = 2;
    Y_PADDING = 10;
    MAX_DATA = 50;
    width: number;
    height: number;
    data: Buffer;
    constructor(width: number, height: number) {
        this.width = width;
        this.height = height;
        this.data = Buffer.create(this.MAX_DATA * 4)
        this.data.fill(0)
    }

    plot (d: number) {
        this.data.shift(4)
        this.data.setNumber(NumberFormat.Float32LE, (this.MAX_DATA * 4) - 4, d);
    }

    paint() {
        let graph = image.create(this.width, this.height)

        // x
        graph.drawLine(this.X_PADDING, this.height - this.Y_PADDING, this.width - this.X_PADDING, this.height - this.Y_PADDING, 15)
        // y
        graph.drawLine(this.X_PADDING, this.Y_PADDING, this.X_PADDING, this.height - this.Y_PADDING, 15)
        scene.setBackgroundImage(graph)

        // min max int 32
        let min:number = 0x7FFFFFFF;
        let max:number = -2147483648;
        for (let i = 0; i < (this.data.length / 4); i++)
        {
            let dataVal = this.data.getNumber(NumberFormat.Float32LE, i*4);
            // console.log(`data: ${dataVal}`)
            if (dataVal < min)
                min = dataVal;
            
            if (dataVal > max)
                max = dataVal;
        }

        if (min == max)
            max = min + 1

        graph.print(Math.roundWithPrecision(min, 3).toString(), this.X_PADDING + 2, this.height - this.Y_PADDING - image.font8.charHeight, 8);
        graph.print(Math.roundWithPrecision(max, 3).toString(), this.X_PADDING + 2, this.Y_PADDING, 8);

        let prevPoint: Point;

        for (let j = 0; j < (this.data.length / 4); j++)
        {
            let dataVal2 = this.data.getNumber(NumberFormat.Float32LE, j * 4);
            let mappedVal = Math.map(dataVal2, min, max, this.Y_PADDING, this.height - this.Y_PADDING);

            let plotPoint = new Point(((this.width - this.X_PADDING) / this.MAX_DATA) * j + this.X_PADDING, this.height - mappedVal);

            if (plotPoint.x != this.X_PADDING)
            {
                graph.drawLine(prevPoint.x, prevPoint.y, plotPoint.x, plotPoint.y, 2);
            }

            prevPoint = plotPoint;
        }
        
    }
}
1 Like

Small update. Supports multiple charts and titles!

class Point {
    x: number;
    y: number;
    constructor(x:number, y: number) {
        this.x = x;
        this.y = y;
    }
}

class Graph {
    X_PADDING = 2;
    Y_PADDING = 10;
    MAX_DATA = 50;
    x: number;
    width: number;
    height: number;
    data: Buffer;
    sprite: Sprite
    title:string
    constructor(x: number, width: number, height: number, title:string=null) {
        this.title = title;
        this.width = width;
        this.height = height;
        this.x = x
        this.data = Buffer.create(this.MAX_DATA * 4)
        this.data.fill(0)
    }

    plot (d: number) {
        this.data.shift(4)
        this.data.setNumber(NumberFormat.Float32LE, (this.MAX_DATA * 4) - 4, d);
    }

    update(graph:Image) {
        if (this.title != null)
            graph.print(this.title, this.x + (this.width / 2), 0, 8)
            
        // x
        graph.drawLine(this.x + this.X_PADDING, this.height - this.Y_PADDING, this.x + this.width - this.X_PADDING, this.height - this.Y_PADDING, 15)
        // y
        graph.drawLine(this.x + this.X_PADDING, this.Y_PADDING, this.x +  this.X_PADDING, this.height - this.Y_PADDING, 15)

        // min max int 32
        let min:number = 0x7FFFFFFF;
        let max:number = -2147483648;
        for (let i = 0; i < (this.data.length / 4); i++)
        {
            let dataVal = this.data.getNumber(NumberFormat.Float32LE, i*4);
            // console.log(`data: ${dataVal}`)
            if (dataVal < min)
                min = dataVal;
            
            if (dataVal > max)
                max = dataVal;
        }

        if (min == max)
            max = min + 1

        graph.print(Math.roundWithPrecision(min, 3).toString(), this.x + this.X_PADDING + 2, this.height - this.Y_PADDING - image.font8.charHeight, 8);
        graph.print(Math.roundWithPrecision(max, 3).toString(), this.x + this.X_PADDING + 2, this.Y_PADDING, 8);

        let prevPoint: Point;

        for (let j = 0; j < (this.data.length / 4); j++)
        {
            let dataVal2 = this.data.getNumber(NumberFormat.Float32LE, j * 4);
            let mappedVal = Math.map(dataVal2, min, max, this.Y_PADDING, this.height - this.Y_PADDING);

            let plotPoint = new Point(((this.width - this.X_PADDING) / this.MAX_DATA) * j + this.X_PADDING, this.height - mappedVal);

            if (plotPoint.x != this.X_PADDING)
            {
                graph.drawLine(this.x + prevPoint.x, prevPoint.y, this.x + plotPoint.x, plotPoint.y, 2);
            }

            prevPoint = plotPoint;
        }
    }
}

To get the chart to update, pass it an image to draw on in update:

let g = new Graph(0, config.DISPLAY_WIDTH, config.DISPLAY_HEIGHT, "H1")
game.onUpdate(function () {
    let graph = image.create(config.DISPLAY_WIDTH, config.DISPLAY_HEIGHT)
    g.update(graph);
    scene.setBackgroundImage(graph)
})
3 Likes