J’aime comment les mathématiques peuvent être utilisées pour représenter des phénomènes que l’on observe dans la nature. Et lorsque que vous combinez ceci avec la programmation, il possible de créer de magnifiques motifs. Pour ce projet, j’ai été inspirées par les plantes grasses et la disposition formée par les feuilles lorsqu’elles croissent. Ceci se nomme la phyllotaxie. Ce motif peut être représenté en utilisant une spirale et l’angle d’or. J’ai utilisé la librairie Processing (p5js) pour programmer ces équations mathématiques et générer des motifs inspirés des succulentes. Le code génére de multiples plantes. Leur dimension et leur disposition sont calculées en utilisant un algorithme permettant de remplir l’espace de façon aléatoire (basé sur les travaux de Paul Bourke et John Shier).
Voici un exemples des images générées.
Et voici une image avec laquelle vous pouvez interagir: déplacez votre souris au-dessus et les succulentes vont croître!
sketch
var sketch = function (p) {
// parameters for the function used to locate all succulents
var circles = [];
var maxRadius = 125;
var minRadius = 35.0;
var gap = 0;
var c = 1.15; // must be larger than 1
var n = 12;
var a0;
var numIter;
spirals = [];
// parameters for the spirals (succulents)
var petals = 0; // 0: convexe, 1: concave
var z = 1.015;
// stroke weight: initial and stroke factor
var s0 = 0.1;
var sf = 0.02;
// stroke color the radius is a ratio of final radiux
var rcol1 = 0.2;
var rcol2 = 0.8;
var init = 0;
p.setup = function () {
p.createCanvas(880, 660);
p.background('#ffffff');
// parameters for the function used to locate all succulents
// initial area to have a1 have maxRadius, a1= a0*pow(n+1,-c)
a0 = p.PI*p.sq(maxRadius)/p.pow(n+1,-c);
// compute the location of all succulents
// num of iterations to have last circles radius minRadius
// reference: Paul Bourke: http://paulbourke.net/texture_colour/randomtile/
numIter = parseInt(p.pow(a0/(p.PI*p.sq(minRadius)), 1/c))-n;
for (var i = 1; i <= numIter; i++) {
var an = a0*p.pow(n+i,-c);
var prad = p.sqrt(an/p.PI);
var px = 0;
var py = 0;
var tries = 0;
var found = false;
while (!found & tries < 1000 ) {
px = p.random(p.width);
py = p.random(p.height);
// check that it is not overlapping previous circle
if (!Overlapping(px, py, prad)) {
found = true;
}
tries++;
}
if (found) {
circles.push(new HCircle(px, py, prad));
}
}
// for all the locations, calculate the parameters of the spiral for the succulents
for (var i=0; i<circles.length; i++) {
spirals.push(new HSpiral(circles[i]));
}
}
p.draw = function () {
for (var i=0; i<circles.length; i++) {
var s = spirals[i];
s.draw();
}
init = 1;
}
p.mouseMoved = function () {
if (init==1) {
// find if mouse on a succulent, if so make it grow
for (var i=0; i<circles.length; i++) {
var c = circles[i];
if (Overlap(c.x, c.y, c.rad, p.mouseX, p.mouseY, 0.0)) {
spirals[i].grow();
// can only be overlapping one circle
break;
}
}
}
}
// Separated(), from Paul Bourke: http://paulbourke.net/texture_colour/randomtile/
function Overlapping(px, py, prad)
{
for (var i=0; i<circles.length; i++) {
var c = circles[i];
if (Overlap(c.x, c.y, c.rad, px, py, prad)) {
return true;
}
}
return false;
}
function Overlap(c1x, c1y, c1rad, c2x, c2y, c2rad) {
var dx,dy;
dx = c1x - c2x;
dy = c1y - c2y;
// adding a gap so they don't touch each other
var radiusSum = c1rad + c2rad + gap;
var centreDistanceSquared = dx*dx + dy*dy;
// circles overlapping if distance between center less than sum of the two radii
if (centreDistanceSquared < radiusSum*radiusSum) {
return true;
}
return false;
}
function HCircle(x, y, rad) {
this.x = x;
this.y = y;
this.rad = rad;
}
function HSpiral(circle) {
this.r0 = p.random(5, 10);
this.cx = circle.x;
this.cy = circle.y;
this.radius = circle.rad;
this.nvertex = parseInt(p.log(this.radius/this.r0)/p.log(z));
var min_nvertex = parseInt(p.log(minRadius/this.r0)/p.log(z));
this.ncurrent = 0;
this.nstep = parseInt(p.random(20,50));
if (this.nstep < min_nvertex) {
this.nstep = min_nvertex;
} else if (this.nstep > this.nvertex) {
this.nstep = this.nvertex;
}
var golden_angle = p.PI*(3- p.sqrt(5)); // golden angle
var theta = p.radians(p.random(0, 45)); //random rotation of the succulent
this.xn = [];
this.yn = [];
this.rn = [];
for (var i = 0; i < this.nvertex; i++) {
var angle = (i*golden_angle + theta)%p.TWO_PI;
this.rn[i] = this.r0*p.pow(z,i); // spiral lattice
this.xn[i] = this.cx + p.cos(angle) * this.rn[i];
this.yn[i] = this.cy + p.sin(angle) * this.rn[i];
}
}
HSpiral.prototype.draw = function() {
for (var i = this.ncurrent; i < this.nstep; i++) {
p.noFill();
p.strokeWeight(sf*this.rn[i] + s0);
var s_col;
p.colorMode(p.RGB);
var col1 = p.color(182,219,25); //#B6DB19
var col2 = p.color(58,9,24); //#3A0918
var rad_ratio = this.rn[i]/this.radius;
if (rad_ratio < rcol1) {
s_col = col1;
} else if (rad_ratio > rcol2) {
s_col = col2;
} else {
var amt =(rad_ratio - rcol1)/(rcol2-rcol1);
s_col = p.lerpColor(col1, col2, amt);
}
p.stroke(s_col);
if (petals == 0) {
p.curveTightness(-0.0);
p.beginShape();
var f = 0.25;
if (i>=8) {
p.curveVertex(this.xn[i-8]+f*(this.cx-this.xn[i-8]), this.yn[i-8]+f*(this.cy-this.yn[i-8]));
p.curveVertex(this.xn[i-8], this.yn[i-8]);
} else {
p.curveVertex(this.cx, this.cy);
p.curveVertex(this.cx, this.cy);
}
p.curveVertex(this.xn[i], this.yn[i]);
if (i>=5) {
p.curveVertex(this.xn[i-5], this.yn[i-5]);
p.curveVertex(this.xn[i-5]+f*(this.cx-this.xn[i-5]), this.yn[i-5]+f*(this.cy-this.yn[i-5]));
} else {
p.curveVertex(this.cx, this.cy);
p.curveVertex(this.cx, this.cy);
}
p.endShape();
} else {
p.curveTightness(0.5);
p.beginShape();
p.curveVertex(this.cx, this.cy);
if (i>=8) {
p.curveVertex(this.xn[i-8], this.yn[i-8]);
} else {
p.curveVertex(this.cx, this.cy);
}
p.curveVertex(this.xn[i], this.yn[i]);
p.curveVertex(this.xn[i]+0.5*(this.xn[i]-this.cx), this.yn[i]+0.5*(this.yn[i]-this.cy));
p.endShape();
p.beginShape();
p.curveVertex(this.xn[i]+0.5*(this.xn[i]-this.cx), this.yn[i]+0.5*(this.yn[i]-this.cy));
p.curveVertex(this.xn[i], this.yn[i]);
if (i>=5) {
p.curveVertex(this.xn[i-5], this.yn[i-5]);
} else {
p.curveVertex(this.cx, this.cy);
}
p.curveVertex(this.cx, this.cy);
p.endShape();
}
}
this.ncurrent = this.nstep;
}
HSpiral.prototype.grow = function() {
if (this.ncurrent < this.nvertex) {
this.nstep = this.ncurrent + 1;
this.draw();
}
}
};
new p5(sketch);
Si vous êtes intéressé par les motifs présents dans la nature, voici une série de vidéos ludiques par Vi Hart (en anglais): Doodling in Math: Spirals, Fibonacci, and Being a Plant.