◆ 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/palette.js · 8.3 Ko

/* ==========================================================================
 * js/palette.js — LA PALETTE DE COULEURS SELON L'HEURE
 * --------------------------------------------------------------------------
 * Toute l'ambiance du diorama (ciel, mer, végétation, nuages…) dépend de
 * l'heure de la journée. Ce module répond à UNE question :
 *
 *     « Quelles couleurs faut-il à 17 h 15 ? »
 *
 * Méthode : on définit quelques « CLÉS » (des heures précises avec leurs
 * couleurs : nuit, aube, plein jour, crépuscule…). Pour une heure donnée,
 * on repère les deux clés qui l'encadrent et on MÉLANGE leurs couleurs
 * (interpolation linéaire, ou « lerp »).
 *
 * Performance : la fonction calculer() ÉCRIT dans un objet `sortie` fourni
 * par l'appelant (créé une seule fois avec creerSortie()). Ainsi on ne
 * fabrique pas de nouveaux objets à chaque calcul.
 *
 * Exemple d'utilisation :
 *   var p = Baie.Palette.creerSortie();      // une seule fois
 *   Baie.Palette.calculer(17.25, p);          // à chaque changement d'heure
 *   forme.color = p.css.mer1;                 // une couleur CSS prête à l'emploi
 * ========================================================================== */

Baie.Palette = (function () {
  'use strict';

  // ---- Les couleurs de chaque moment --------------------------------------
  // Chaque « ambiance » est une table nom -> couleur hexadécimale.
  // `etoiles` (0 à 1) dit si le ciel est assez sombre pour voir les étoiles.
  var NUIT = {
    cielHaut: '#050b26', cielBas: '#1a2354',
    mer1: '#0a1a3c', mer2: '#142c5c', ecume: '#5a6fa8',
    herbe: '#1d3b2c', sable: '#6c6b78', palme: '#1b4d38',
    nuage: '#3c4670', soleil: '#ffcc66', lune: '#e8eefc',
    etoiles: 1,
  };
  var AUBE = {
    cielHaut: '#4b5c9e', cielBas: '#ffb18c',
    mer1: '#2b5c88', mer2: '#d98c80', ecume: '#ffe3d0',
    herbe: '#5f9460', sable: '#dcb99c', palme: '#3b7d55',
    nuage: '#ffc9a8', soleil: '#ffb04d', lune: '#e8eefc',
    etoiles: 0.15,
  };
  var JOUR = {
    cielHaut: '#3f9be6', cielBas: '#c4e8ff',
    mer1: '#168bb3', mer2: '#34b8cc', ecume: '#ffffff',
    herbe: '#6fcf5a', sable: '#f2deaa', palme: '#2f9e50',
    nuage: '#ffffff', soleil: '#ffe36a', lune: '#e8eefc',
    etoiles: 0,
  };
  var CREPUSCULE = {
    cielHaut: '#5d4092', cielBas: '#ff7f5e',
    mer1: '#2c4b7c', mer2: '#dd6f5d', ecume: '#ffd2b8',
    herbe: '#51804c', sable: '#dcaa8a', palme: '#306d47',
    nuage: '#ff9c7c', soleil: '#ff7a3a', lune: '#e8eefc',
    etoiles: 0.1,
  };
  var CREPUSCULE_TARDIF = {
    cielHaut: '#1f2055', cielBas: '#80406c',
    mer1: '#15274f', mer2: '#313c70', ecume: '#a98fb8',
    herbe: '#2d4b37', sable: '#8c7782', palme: '#204e39',
    nuage: '#5b4b7c', soleil: '#ff6a30', lune: '#e8eefc',
    etoiles: 0.6,
  };

  // La liste des clés : { h: heure, c: ambiance }, triée par heure croissante.
  // Entre 8 h 30 et 16 h, la palette reste celle du plein jour.
  var CLES = [
    { h: 0,    c: NUIT },
    { h: 5,    c: NUIT },
    { h: 6.5,  c: AUBE },
    { h: 8.5,  c: JOUR },
    { h: 16,   c: JOUR },
    { h: 18,   c: CREPUSCULE },
    { h: 19.5, c: CREPUSCULE_TARDIF },
    { h: 21,   c: NUIT },
    { h: 24,   c: NUIT },
  ];

  // Les noms des couleurs gérées (tous sauf `etoiles`, qui est un nombre).
  var NOMS = ['cielHaut', 'cielBas', 'mer1', 'mer2', 'ecume', 'herbe',
              'sable', 'palme', 'nuage', 'soleil', 'lune'];

  // ---- Conversion hexadécimale -> [rouge, vert, bleu] ----------------------
  /**
   * Transforme '#ffb18c' en [255, 177, 140].
   * @param {string} hex Couleur '#rrggbb'.
   * @returns {number[]} Trois nombres de 0 à 255.
   */
  function hexVersRgb(hex) {
    var n = parseInt(hex.slice(1), 16);          // base 16
    return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
  }

  // On convertit UNE FOIS pour toutes les clés (au chargement du fichier),
  // pour ne pas refaire ce travail 60 fois par seconde.
  CLES.forEach(function (cle) {
    cle.rgb = {};
    NOMS.forEach(function (nom) { cle.rgb[nom] = hexVersRgb(cle.c[nom]); });
  });

  // ---- Petits outils de couleur --------------------------------------------
  /** [r, v, b] -> 'rgb(r,v,b)' (chaîne utilisable par Zdog et le CSS). */
  function css(rgb) {
    return 'rgb(' + Math.round(rgb[0]) + ',' + Math.round(rgb[1]) + ',' + Math.round(rgb[2]) + ')';
  }

  /** [r, v, b] + opacité -> 'rgba(r,v,b,a)' (couleur semi-transparente). */
  function cssAlpha(rgb, alpha) {
    return 'rgba(' + Math.round(rgb[0]) + ',' + Math.round(rgb[1]) + ',' + Math.round(rgb[2]) + ',' + alpha + ')';
  }

  /**
   * Mélange deux couleurs.
   * @param {number[]} a    Première couleur [r, v, b].
   * @param {number[]} b    Deuxième couleur.
   * @param {number}   t    0 = tout `a`, 1 = tout `b`.
   * @param {number[]} out  Tableau de travail réutilisé (évite d'en créer un).
   * @returns {string} La couleur obtenue, en CSS.
   */
  function melanger(a, b, t, out) {
    out[0] = a[0] + (b[0] - a[0]) * t;
    out[1] = a[1] + (b[1] - a[1]) * t;
    out[2] = a[2] + (b[2] - a[2]) * t;
    return css(out);
  }

  /**
   * Éclaircit (f > 0) ou assombrit (f < 0) une couleur.
   * @param {number[]} rgb  Couleur de départ.
   * @param {number}   f    De -1 (noir) à +1 (blanc).
   * @param {number[]} out  Tableau de travail réutilisé.
   * @returns {string} La couleur obtenue, en CSS.
   */
  function nuancer(rgb, f, out) {
    var cible = f >= 0 ? 255 : 0;
    var force = Math.abs(f);
    out[0] = rgb[0] + (cible - rgb[0]) * force;
    out[1] = rgb[1] + (cible - rgb[1]) * force;
    out[2] = rgb[2] + (cible - rgb[2]) * force;
    return css(out);
  }

  // ---- Création de l'objet « sortie » --------------------------------------
  /**
   * Crée l'objet dans lequel calculer() écrira ses résultats.
   * À appeler UNE SEULE FOIS.
   * @returns {Object} sortie = {
   *   heure, phase (texte), etoiles (0..1),
   *   rgb: { nom: [r,v,b] },   // pour faire des calculs
   *   css: { nom: 'rgb(...)' } // pour colorer directement
   * }
   */
  function creerSortie() {
    var sortie = { heure: 0, phase: '', etoiles: 0, rgb: {}, css: {} };
    NOMS.forEach(function (nom) {
      sortie.rgb[nom] = [0, 0, 0];
      sortie.css[nom] = '#000';
    });
    return sortie;
  }

  // ---- Nom de la phase de la journée ---------------------------------------
  /**
   * @param {number} h Heure décimale (0 à 24).
   * @returns {string} 'Aube', 'Jour', 'Crépuscule' ou 'Nuit'.
   */
  function nomPhase(h) {
    if (h >= 5 && h < 7.5) { return 'Aube'; }
    if (h >= 7.5 && h < 16.5) { return 'Jour'; }
    if (h >= 16.5 && h < 19.5) { return 'Crépuscule'; }
    return 'Nuit';
  }

  // ---- Le calcul principal -------------------------------------------------
  /**
   * Calcule la palette complète pour une heure.
   *
   * @param {number} heure   Heure décimale : 14,5 = 14 h 30. Valeur ramenée dans [0 ; 24[.
   * @param {Object} sortie  Objet créé par creerSortie() ; il est REMPLI par cette fonction.
   * @returns {Object} Le même objet `sortie` (pratique pour enchaîner).
   */
  function calculer(heure, sortie) {
    // Zdog.modulo ramène toujours dans [0 ; 24[, même pour -3 ou 27.
    var h = Zdog.modulo(heure, 24);

    // On cherche i tel que CLES[i].h <= h < CLES[i+1].h
    var i = 0;
    while (i < CLES.length - 2 && h >= CLES[i + 1].h) { i++; }
    var a = CLES[i];
    var b = CLES[i + 1];

    // t = avancement entre les deux clés (0 au début, 1 à la fin).
    var t = (h - a.h) / (b.h - a.h);

    // On « adoucit » t avec easeInOut : les transitions de couleur démarrent
    // et finissent en douceur plutôt qu'à vitesse constante.
    var te = Zdog.easeInOut(t, 2);

    for (var n = 0; n < NOMS.length; n++) {
      var nom = NOMS[n];
      var ra = a.rgb[nom], rb = b.rgb[nom], sortieRgb = sortie.rgb[nom];
      sortieRgb[0] = ra[0] + (rb[0] - ra[0]) * te;
      sortieRgb[1] = ra[1] + (rb[1] - ra[1]) * te;
      sortieRgb[2] = ra[2] + (rb[2] - ra[2]) * te;
      sortie.css[nom] = css(sortieRgb);
    }

    sortie.heure = h;
    sortie.etoiles = Zdog.lerp(a.c.etoiles, b.c.etoiles, te);
    sortie.phase = nomPhase(h);
    return sortie;
  }

  // Ce que le module offre aux autres fichiers.
  return {
    creerSortie: creerSortie,
    calculer: calculer,
    css: css,
    cssAlpha: cssAlpha,
    melanger: melanger,
    nuancer: nuancer,
    hexVersRgb: hexVersRgb,
    nomPhase: nomPhase,
  };
})();