◆ Portail Antalaha

Annuaire › Code ouvert

Maison 3D pour une agence immobilière

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/maison.js · 10.2 Ko

/* ==========================================================================
 * js/maison.js — LE CORPS DE LA MAISON : murs, fenêtres, portes et toit
 * --------------------------------------------------------------------------
 * Fonction fabrique : Maison.creerCorps(parent, facade) construit
 *   - le soubassement (petit socle en pierre) ;
 *   - le rez-de-chaussée (une boîte) ;
 *   - l'étage (une boîte moins profonde : devant, il y aura la véranda) ;
 *   - les fenêtres et les portes ;
 *   - le toit à deux pans en tôle, avec ses deux pignons triangulaires.
 *
 * Croquis vu de côté (y vers le bas, donc les hauteurs sont NÉGATIVES) :
 *
 *            faîtage  y = -136
 *             /\
 *            /  \          toit
 *    -------/----\-------   bords du toit y = -99
 *   |    étage    |         de y = -99 à -52
 *   |-------------|         plancher y = -52
 *   | rez-de-ch.  |         de y = -52 à -6
 *   |_____________|  socle  de y = -6 à 0 (le sol)
 *
 * Dépend de : Zdog, Maison.DIM, Maison.COULEURS, Maison.peindreBoite,
 * Maison.creerFenetre, Maison.nuancer (config.js et outils.js).
 * ========================================================================== */

(function () {
  'use strict';

  var TAU = Zdog.TAU;

  /**
   * Construit les murs, ouvertures et le toit.
   *
   * @param {Zdog.Anchor} parent Où accrocher la maison (un Group).
   * @param {Object} facade Un élément de Maison.FACADES (clé « mur » = couleur).
   * @returns {Object} { rez, etage, pignons } : les pièces qu'il faudra repeindre
   *                   quand l'utilisateur change la couleur de la façade.
   */
  function creerCorps(parent, facade) {
    var D = Maison.DIM;
    var C = Maison.COULEURS;

    // ---- Calculs de hauteurs (rappel : le haut est NÉGATIF) ----
    var yHautSocle = -D.soubassement;                         // -6
    var yHautRez = yHautSocle - D.hauteurRez;                 // -52
    var yHautEtage = yHautRez - D.hauteurEtage;               // -99
    var zAvant = D.profondeur / 2;                            // +40 (côté spectateur)
    var zArriere = -D.profondeur / 2;                         // -40
    var zAvantEtage = zArriere + D.profondeurEtage;           // +14 : façade de l'étage

    // 1) LE SOUBASSEMENT ----------------------------------------------------
    // Une boîte plate, un peu plus large que la maison, en pierre grise.
    // Une Box est centrée sur sa position (translate) : son centre vertical
    // est donc à -3 pour couvrir de y = -6 à y = 0.
    new Zdog.Box({
      addTo: parent,
      width: D.largeur + 6,
      height: D.soubassement,
      depth: D.profondeur + 6,
      translate: { y: -D.soubassement / 2 },
      stroke: 1,
      color: C.pierre,
      topFace: Maison.nuancer(C.pierre, 0.2),
    });

    // 2) LE REZ-DE-CHAUSSÉE -------------------------------------------------
    // Centre de la boîte = milieu entre le haut du socle (-6) et le plancher (-52).
    var rez = new Zdog.Box({
      addTo: parent,
      width: D.largeur,
      height: D.hauteurRez,
      depth: D.profondeur,
      translate: { y: (yHautSocle + yHautRez) / 2 },
      stroke: 1,
      color: facade.mur,
    });
    Maison.peindreBoite(rez, facade.mur);

    // 3) L'ÉTAGE ------------------------------------------------------------
    // Moins profond que le rez-de-chaussée : il part de l'arrière (z = -40)
    // et s'arrête à z = +14. Son centre en z est donc au milieu : -13.
    var etage = new Zdog.Box({
      addTo: parent,
      width: D.largeur,
      height: D.hauteurEtage,
      depth: D.profondeurEtage,
      translate: { y: (yHautRez + yHautEtage) / 2, z: (zArriere + zAvantEtage) / 2 },
      stroke: 1,
      color: facade.mur,
    });
    Maison.peindreBoite(etage, facade.mur);

    // 4) FENÊTRES ET PORTES DU REZ-DE-CHAUSSÉE ------------------------------
    // On pose chaque ouverture 0,3 unité DEVANT le mur pour qu'elle ne soit
    // pas « mangée » par lui (sinon Zdog hésiterait sur qui dessiner d'abord).
    var xMur = D.largeur / 2 + 0.3;
    var yRez = (yHautSocle + yHautRez) / 2 + 1;              // milieu du rez, un peu plus bas

    Maison.creerFenetre(parent, { x: -44, y: yRez, z: zAvant + 0.3, rotY: 0, volets: true });
    Maison.creerFenetre(parent, { x: 52, y: yRez, z: zAvant + 0.3, rotY: 0, largeur: 12 });

    // La porte d'entrée : un simple rectangle brun (largeur 18, hauteur 34)
    // dont le bas touche le socle (y = -6) : son centre est à -6 - 17 = -23.
    new Zdog.Rect({
      addTo: parent, width: 18, height: 34, stroke: 1, fill: true,
      color: C.boisFonce, backface: false,
      translate: { x: -12, y: yHautSocle - 17, z: zAvant + 0.3 },
    });
    // Un petit bouton de porte : un point épais jaune.
    new Zdog.Shape({
      addTo: parent, stroke: 2, color: '#e8c15a', backface: false,
      translate: { x: -6, y: yHautSocle - 17, z: zAvant + 0.8 },
    });

    // Fenêtres sur les côtés (rotY = ±TAU/4) et à l'arrière (rotY = TAU/2).
    Maison.creerFenetre(parent, { x: xMur, y: yRez, z: 0, rotY: TAU / 4 });
    Maison.creerFenetre(parent, { x: -xMur, y: yRez, z: 0, rotY: -TAU / 4 });
    Maison.creerFenetre(parent, { x: -30, y: yRez, z: zArriere - 0.3, rotY: TAU / 2 });
    Maison.creerFenetre(parent, { x: 30, y: yRez, z: zArriere - 0.3, rotY: TAU / 2 });

    // 5) FENÊTRES ET PORTE DE L'ÉTAGE ---------------------------------------
    var yEtage = (yHautRez + yHautEtage) / 2 + 1;            // milieu de l'étage
    var zFacadeEtage = zAvantEtage + 0.3;                    // devant le mur de l'étage

    Maison.creerFenetre(parent, { x: -40, y: yEtage, z: zFacadeEtage, rotY: 0, volets: true });
    Maison.creerFenetre(parent, { x: 40, y: yEtage, z: zFacadeEtage, rotY: 0, volets: true });
    // Porte-fenêtre de l'étage : une porte vitrée qui donne sur la véranda.
    new Zdog.Rect({
      addTo: parent, width: 20, height: 36, stroke: 1, fill: true,
      color: C.boisFonce, backface: false,
      translate: { y: yHautRez - 18, z: zFacadeEtage },
    });
    new Zdog.Rect({
      addTo: parent, width: 14, height: 28, stroke: 1, fill: true,
      color: C.vitre, backface: false,
      translate: { y: yHautRez - 19, z: zFacadeEtage + 0.4 },
    });

    Maison.creerFenetre(parent, { x: xMur, y: yEtage, z: -13, rotY: TAU / 4 });
    Maison.creerFenetre(parent, { x: -xMur, y: yEtage, z: -13, rotY: -TAU / 4 });
    Maison.creerFenetre(parent, { x: -30, y: yEtage, z: zArriere - 0.3, rotY: TAU / 2 });
    Maison.creerFenetre(parent, { x: 30, y: yEtage, z: zArriere - 0.3, rotY: TAU / 2 });

    // 6) LE TOIT ------------------------------------------------------------
    var pignons = creerToit(parent, facade, yHautEtage);

    return { rez: rez, etage: etage, pignons: pignons };
  }

  /**
   * Construit le toit à deux pans (« en bâtière ») et les deux pignons.
   *
   * Un « pan » est un quadrilatère : on le dessine avec un Shape dont le path
   * contient 4 points 3D. Le faîtage (la crête) est à z = 0, les bords bas
   * (les égouts) sont à l'avant et à l'arrière.
   *
   * @param {Zdog.Anchor} parent     Où accrocher le toit.
   * @param {Object} facade          Pour colorer les pignons comme les murs.
   * @param {number} yEgout          Hauteur (négative) du haut des murs de l'étage.
   * @returns {Zdog.Shape[]} Les deux pignons (à repeindre au changement de façade).
   */
  function creerToit(parent, facade, yEgout) {
    var D = Maison.DIM;
    var C = Maison.COULEURS;

    var demiLong = D.largeur / 2 + D.debordToit;      // demi-longueur du toit (x)
    var zEgout = D.profondeur / 2 + D.debordToit;     // bord avant du toit : +44
    var yE = yEgout + 1;                              // bords bas du toit
    var yF = yE - D.hauteurToit;                      // faîtage (crête)

    // On ajoute chaque pan avec ses nervures dans un Group : l'ordre de dessin
    // est alors garanti (d'abord le pan, ensuite les nervures par-dessus).
    [{ z: zEgout, couleur: C.toit }, { z: -zEgout, couleur: C.toitArriere }].forEach(function (pan) {
      var groupe = new Zdog.Group({ addTo: parent });

      // Le pan de toit : 4 coins (bas gauche, bas droite, haut droite, haut gauche).
      new Zdog.Shape({
        addTo: groupe,
        path: [
          { x: -demiLong, y: yE, z: pan.z },
          { x: demiLong, y: yE, z: pan.z },
          { x: demiLong, y: yF, z: 0 },
          { x: -demiLong, y: yF, z: 0 },
        ],
        stroke: 3,
        fill: true,
        color: pan.couleur,
      });

      // Les nervures de la tôle ondulée : une ligne tous les 8 unités.
      // (Une boucle for évite d'écrire 16 formes à la main.)
      for (var x = -demiLong + 8; x < demiLong; x += 8) {
        new Zdog.Shape({
          addTo: groupe,
          path: [{ x: x, y: yE, z: pan.z }, { x: x, y: yF, z: 0 }],
          closed: false,
          stroke: 0.8,
          color: Maison.nuancer(pan.couleur, -0.25),
        });
      }
    });

    // La crête du toit : un trait épais et sombre tout le long du faîtage.
    new Zdog.Shape({
      addTo: parent,
      path: [{ x: -demiLong, y: yF, z: 0 }, { x: demiLong, y: yF, z: 0 }],
      closed: false,
      stroke: 4,
      color: C.faitage,
    });

    // Les pignons : deux triangles qui ferment le toit à gauche et à droite,
    // juste à l'intérieur du débord (x = ±(largeur/2 + 2)).
    var pignons = [];
    [-1, 1].forEach(function (cote) {
      var x = cote * (D.largeur / 2 + 2);
      pignons.push(new Zdog.Shape({
        addTo: parent,
        path: [
          { x: x, y: yE, z: -zEgout + 2 },
          { x: x, y: yE, z: zEgout - 2 },
          { x: x, y: yF, z: 0 },
        ],
        stroke: 2,
        fill: true,
        color: Maison.nuancer(facade.mur, -0.1),
      }));
    });
    return pignons;
  }

  /**
   * Repeint la maison avec une nouvelle façade (sans rien reconstruire).
   *
   * @param {Object} corps  L'objet renvoyé par creerCorps().
   * @param {Object} facade Un élément de Maison.FACADES.
   */
  function peindreFacade(corps, facade) {
    Maison.peindreBoite(corps.rez, facade.mur);
    Maison.peindreBoite(corps.etage, facade.mur);
    corps.pignons.forEach(function (p) {
      p.color = Maison.nuancer(facade.mur, -0.1);
    });
  }

  Maison.creerCorps = creerCorps;
  Maison.peindreFacade = peindreFacade;
})();