Chapter 7: Draw the rosetta

JavaScript

Raphael.fn.rosetta = function(x,y,rx,ry,N) {
    if (N == 0) {
        console.log("no diving by zero, please"); return;
    }
    var angle = 360 / N; // negative values of N are fine
    var path = "M" + x + "," + y;
    
    for (var c = 0; c < N; c += 1) {
        // need angle for each leaf of rosetta in radians
        var theta = angle * c * Math.PI / 180;
        // coords of farthest point from center for this leaf
        var dx = x + 2 * rx * Math.cos(theta),
            dy = y + 2 * rx * Math.sin(theta);
        path += "A" + rx + "," + ry + " " + angle*c + " 1,1 " + dx + "," + dy;
        path += "A" + rx + "," + ry + " " + angle * c + " 1,1 " + x + "," + y;
    }
    var rosetta = paper.path(path);        
    return rosetta;
}

var paper = Raphael(0, 0, 500, 500);

var trail = paper.path("M0,0").attr({
    stroke: "red",
    'stroke-width': 3
});

paper.customAttributes.progress = function (v) {
    var path = this.data("mypath"),
        attrs = this.attr(),
        offset = { x: 0, y: 0 };

    if (!path) {
        return {
            transform: "t0,0"
        };
    }    
    
    if (attrs.hasOwnProperty("width")) {
        offset.x = -this.attr("width") / 2;
        offset.y = -this.attr("height") / 2;
    }
    
    var len = path.getTotalLength();
    var point = path.getPointAtLength(v * len);

    var trail = this.data("mytrail");

    if (trail) {
        trail.attr("path", path.getSubpath(0, v * len));
    }
    
    return {
        transform: "t" + (point.x + offset.x) + "," + (point.y + offset.y) + "r" + (point.alpha < 180 ? point.alpha + 180 : point.alpha)
    };
};

var rose = paper.rosetta(120, 120, 55, 35, 6).attr("stroke", "#FFF");

var shape = paper.rect(0, 0, 20, 10).attr("fill", "green");
var point = rose.getPointAtLength(800);
var trail = paper.path("M0,0").attr({
    stroke: "red",
    'stroke-width': 3
});

shape.data("mypath", rose);
shape.data("mytrail", trail);
shape.attr("progress", 0);
shape.animate({ progress: 1 },...