Annuaire › Code ouvert
Code publié par son auteur, en lecture seule. « Copier dans mon espace » crée un nouveau site à vous (non publié) que vous pouvez modifier librement.
js/palmier.js · 7.3 Ko
/* ==========================================================================
* js/palmier.js — LES PALMIERS (générés par une fonction)
* --------------------------------------------------------------------------
* Baie.creerPalmier(parent, options) fabrique un palmier complet :
* - un TRONC courbe : un Shape dont le path contient une courbe de Bézier ;
* - une COURONNE de palmes : chaque palme est un Shape rempli, fait de deux
* courbes de Bézier (le dessus et le dessous de la feuille) ;
* - quelques NOIX DE COCO : des points (Shape sans path) très épais = sphères.
*
* Le même code produit des palmiers différents selon les options (hauteur,
* inclinaison, graine du hasard) : c'est la force d'une FONCTION FABRIQUE.
*
* Une courbe de Bézier se décrit avec 3 points :
* { bezier: [ pointDeControle1, pointDeControle2, pointFinal ] }
* Le tracé part du point précédent, est « attiré » vers les deux points de
* contrôle, et arrive au point final.
*
* Dépend de : Zdog, Baie.Palette (palette.js).
* ========================================================================== */
(function () {
'use strict';
var TAU = Zdog.TAU;
// ---- Constantes réglables -------------------------------------------------
var NB_PALMES = 8; // nombre de palmes par couronne
var COULEUR_TRONC = '#8a5d34';
var COULEUR_COCO = '#5b3a1c';
/**
* Générateur de hasard reproductible (mulberry32) : même graine = même palmier
* à chaque chargement de la page (Math.random() changerait tout le temps).
* @param {number} graine Un entier.
* @returns {function(): number} Une fonction qui renvoie un nombre entre 0 et 1.
*/
function hasardAvecGraine(graine) {
var a = graine >>> 0;
return function () {
a = (a + 0x6D2B79F5) >>> 0;
var t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
/**
* Construit un palmier.
*
* @param {Zdog.Anchor} parent Où l'accrocher.
* @param {Object} [o] Options :
* x, z position au sol ;
* hauteur hauteur du tronc (défaut 46) ;
* penche décalage horizontal du sommet = inclinaison (défaut 12) ;
* orientation angle (radians) autour de l'axe vertical : vers où il penche ;
* graine entier qui change la forme des palmes.
* @returns {Object} { racine, mettreAJour(t), appliquerPalette(p) }
*/
function creerPalmier(parent, o) {
o = o || {};
var h = o.hauteur || 46;
var penche = (o.penche === undefined) ? 12 : o.penche;
var hasard = hasardAvecGraine(o.graine || 1);
// Phase propre à chaque palmier : ils ne se balancent pas tous ensemble.
var phase = hasard() * TAU;
// Le Group racine : on peut le déplacer, le tourner, le balancer d'un bloc.
// (updateSort: false = les enfants sont dessinés dans l'ordre d'ajout :
// d'abord le tronc, ensuite la couronne par-dessus.)
var racine = new Zdog.Group({
addTo: parent,
translate: { x: o.x || 0, z: o.z || 0 },
rotate: { y: o.orientation || 0 },
});
// 1) LE TRONC ----------------------------------------------------------
// Une seule courbe de Bézier du pied (0, 0) au sommet (penche, -h).
// Rappel : le haut est à y NÉGATIF. Les deux points de contrôle donnent
// la courbure : le tronc part droit, puis s'incline vers le sommet.
new Zdog.Shape({
addTo: racine,
path: [
{ x: 0, y: 0 }, // pied du tronc
{ bezier: [
{ x: 0, y: -h * 0.45 }, // 1er contrôle : monte droit
{ x: penche * 0.7, y: -h * 0.7 }, // 2e contrôle : s'incline
{ x: penche, y: -h }, // sommet
] },
],
closed: false,
stroke: 5,
color: COULEUR_TRONC,
});
// 2) LA COURONNE -------------------------------------------------------
// Un Group qui trie ses palmes (updateSort: true) : quand on tourne la
// vue, celles de derrière sont dessinées avant celles de devant.
var couronne = new Zdog.Group({
addTo: racine,
translate: { x: penche, y: -h },
updateSort: true,
});
var palmes = []; // les formes (pour changer leur couleur)
var inclinaisons = []; // l'inclinaison de base de chaque palme (pour l'agiter)
for (var i = 0; i < NB_PALMES; i++) {
var angle = (i / NB_PALMES) * TAU + hasard() * 0.4; // répartition en étoile
var L = 30 + hasard() * 12; // longueur de la palme
var inclinaison = (hasard() - 0.5) * 0.5; // petite variété d'inclinaison
// La palme est dessinée dans un plan vertical (axes x et y), x = vers
// l'extérieur. Elle monte puis retombe (y final POSITIF = vers le bas).
var palme = new Zdog.Shape({
addTo: couronne,
path: [
{ x: 0, y: 0 }, // attache
{ bezier: [ // dessus de la feuille
{ x: L * 0.3, y: -L * 0.38 },
{ x: L * 0.7, y: -L * 0.32 },
{ x: L, y: L * 0.28 },
] },
{ bezier: [ // dessous : retour à l'attache
{ x: L * 0.7, y: -L * 0.12 },
{ x: L * 0.3, y: -L * 0.14 },
{ x: 0, y: 0 },
] },
],
fill: true, // on remplit la feuille
stroke: 1.6,
rotate: { y: angle, z: inclinaison }, // z = redresse/abaisse ; y = tourne autour du tronc
color: '#2f9e50', // remplacée par appliquerPalette()
});
palmes.push(palme);
inclinaisons.push(inclinaison);
}
// 3) LES NOIX DE COCO --------------------------------------------------
// Un Shape SANS path = un point ; son `stroke` est le diamètre de la sphère.
for (var c = 0; c < 3; c++) {
new Zdog.Shape({
addTo: couronne,
translate: { x: (hasard() - 0.5) * 8, y: 3, z: (hasard() - 0.5) * 8 },
stroke: 6,
color: COULEUR_COCO,
});
}
/**
* Balance le palmier dans le vent. À appeler à chaque image.
* Seulement des nombres modifiés : aucun objet créé.
* @param {number} t Temps écoulé en secondes.
*/
function mettreAJour(t) {
// Le tronc entier oscille très légèrement autour de son pied (axe z).
racine.rotate.z = 0.035 * Math.sin(t * 1.1 + phase);
// Chaque palme frémit autour de son inclinaison de base.
for (var i = 0; i < palmes.length; i++) {
palmes[i].rotate.z = inclinaisons[i] + 0.12 * Math.sin(t * 2.3 + phase + i);
}
}
var tmp = [0, 0, 0]; // tableau de travail réutilisé
/**
* Colore les palmes selon la palette : une palme sur deux est un peu plus sombre.
* @param {Object} p Palette (Baie.Palette.calculer()).
*/
function appliquerPalette(p) {
var sombre = Baie.Palette.nuancer(p.rgb.palme, -0.22, tmp);
for (var i = 0; i < palmes.length; i++) {
palmes[i].color = (i % 2 === 0) ? p.css.palme : sombre;
}
}
return { racine: racine, mettreAJour: mettreAJour, appliquerPalette: appliquerPalette };
}
Baie.creerPalmier = creerPalmier;
})();