Generate an SVG sunburst from JavaScript

I want to generate a sunburst to use with a laser-cutting service, like below:

This has a couple of important features:

  • It’s a single path, for the laser to use
  • The rays on the sun can intersect a bounding box, including the corners
  • This will produce two interlocking parts (e.g. for printmaking, these can be inked separately, and combined)

The code is below – this works with Paper.js:

var postyle = { fillColor:'grey', strokeColor: 'black'}

const size = 200;
const rect = [0, 0, [ size, size]];
const ratio = 0.25;
const radius = size * ratio;
const spokes = 48;
const pi = 3.14;

const center = [0, 0]

var path = new paper.Path();
path.strokeColor = "#000";
path.closed = true;

function ip(a) {
    return [
        radius * Math.cos(a),
        radius * Math.sin(a)
    ];
}

function intersect(x1, y1, x2, y2, mixX, minY, maxX, maxY) {
    var point = rectangleIntersect(maxX - x1, maxY - y1, x2 - x1, y2 - y1);
    return point && { x: point.x + x1, y: point.y + y1 };
}

function rectangleIntersect(w, h, x, y) {
    var sx = x > 0 ? 1 : -1;
    var sy = y > 0 ? 1 : -1;

    x *= sx;
    y *= sy;

    if (x < w && y < h) return null;

    var m = x * h;
    var n = y * w;

    if (m < n) w = m / y;
    if (m > n) h = n / x;

    return [ Math.round( sx * w ), Math.round(sy * h ) ];
}

function outer(a) {
    return rectangleIntersect(
        size,
        size,
        100000 * Math.cos(a) + size/2,
        100000 * Math.sin(a) + size/2,
    );
}

function maybeCorner(p1, p2, a1) {
    const corners = [
        [1 * size, -1 * size],
        [1 * size, 1 * size],
        [-1 * size, 1 * size],
        [-1 * size, -1 * size], 
    ];
    
    if (p1[0] != p2[0] && 
        p1[1] != p2[1]) {
        const idx = Math.round(2*a1/pi) % 4;
        return corners[idx];
    }
}

for (let i = 0; i < spokes; i++) {
    const a1 = pi / spokes * (2 * i);
    const a2 = pi / spokes * (2 * i + 1);
    
    const inner1 = ip(a1);
    const inner2 = ip(a2);
    const outer1 = outer(a1);
    const outer2 = outer(a2);
    
    const points = [
        inner1, 
        outer1,
        maybeCorner(outer1, outer2, a1),
        outer2,
        inner2].filter(
        (p) => !!p        
    );
        
    points.map(
        ([x, y]) => {
            path.add(new paper.Point(x + center[0], y + center[1]));
        });
}

Leave a Reply

Your email address will not be published. Required fields are marked *