◆ Portail Antalaha

Annuaire › Code ouvert

Baie d'Antalaha : diorama 3D animé jour et nuit

tutorial · Voir le site ↗
Connectez-vous pour copier ce site

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;
})();