Skip to content

Instantly share code, notes, and snippets.

@kevinkub
Last active August 27, 2025 01:41
Show Gist options
  • Save kevinkub/b74f9c16f050576ae760a7730c19b8e2 to your computer and use it in GitHub Desktop.
Save kevinkub/b74f9c16f050576ae760a7730c19b8e2 to your computer and use it in GitHub Desktop.

Revisions

  1. kevinkub revised this gist Oct 28, 2020. No changes.
  2. kevinkub created this gist Oct 28, 2020.
    74 changes: 74 additions & 0 deletions scriptable-line-chart.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    class LineChart {
    // LineChart by https://kevinkub.de/

    constructor(width, height, values) {
    this.ctx = new DrawContext();
    this.ctx.size = new Size(width, height);
    this.values = values;
    }

    _calculatePath() {
    let maxValue = Math.max(...this.values);
    let minValue = Math.min(...this.values);
    let difference = maxValue - minValue;
    let count = this.values.length;
    let step = this.ctx.size.width / (count - 1);
    let points = this.values.map((current, index, all) => {
    let x = step*index;
    let y = this.ctx.size.height - (current - minValue) / difference * this.ctx.size.height;
    return new Point(x, y);
    });
    return this._getSmoothPath(points);
    }

    _getSmoothPath(points) {
    let path = new Path();
    path.move(new Point(0, this.ctx.size.height));
    path.addLine(points[0]);
    for(let i = 0; i < points.length-1; i++) {
    let xAvg = (points[i].x + points[i+1].x) / 2;
    let yAvg = (points[i].y + points[i+1].y) / 2;
    let avg = new Point(xAvg, yAvg);
    let cp1 = new Point((xAvg + points[i].x) / 2, points[i].y);
    let next = new Point(points[i+1].x, points[i+1].y);
    let cp2 = new Point((xAvg + points[i+1].x) / 2, points[i+1].y);
    path.addQuadCurve(avg, cp1);
    path.addQuadCurve(next, cp2);
    }
    path.addLine(new Point(this.ctx.size.width, this.ctx.size.height));
    path.closeSubpath();
    return path;
    }

    configure(fn) {
    let path = this._calculatePath();
    if(fn) {
    fn(this.ctx, path);
    } else {
    this.ctx.addPath(path);
    this.ctx.fillPath(path);
    }
    return this.ctx;
    }

    }


    // Demo
    let data = [20, 10, 15, 5, 10, 30, 20, 10, 10, 10, 20, 10];
    let widget = new ListWidget();
    let chart = new LineChart(400, 200, data).configure((ctx, path) => {
    ctx.opaque = false;
    ctx.setFillColor(new Color("888888", .5));
    ctx.addPath(path);
    ctx.fillPath(path);
    }).getImage();
    widget.addText("Hello Charts!");
    widget.addSpacer();
    let image = widget.addImage(chart);

    Script.setWidget(widget);
    if (!config.runsInWidget) {
    await widget.presentSmall();
    }
    Script.complete();