legacy/outils/3D/Solide.js

import $ from 'jquery'
import { j3pBoule, j3pDetruit, j3pElement } from 'src/legacy/core/functions'
import { j3pInverseMatrice, j3pProduitMatVec, j3pRotation, j3pVectoriel } from 'src/legacy/outils/3D/3D'
import { j3pCreeSegment, j3pCreeSVG, j3pCreeTexte, j3pPolygone } from 'src/legacy/core/functionsSvg'
/**
 * objet utilisé nulle part, ne fonctionne peut-être pas
 * @param idconteneur
 * @param objet
 * @constructor
 */
function Solide (idconteneur, objet) {
  this.idconteneur = idconteneur

  // j3pElement(idconteneur).ref= ref
  this.sommets = objet.sommets
  this.faces = objet.faces
  this.repere = objet.repere

  // si nomsommets undefined les sommets sont nommés automatiquement "somk" (mais étiquettes non affichées)
  if (typeof (objet.nomsommets) === 'undefined') {
    this.nomsommets = ['']
    for (let k = 1; k < this.sommets.length; k++) {
      this.nomsommets.push('som' + k)
    }
  } else {
    this.nomsommets = objet.nomsommets
  }

  if (typeof (objet.mef) === 'undefined') {
    this.mef = { couleursommets: { defaut: 'F00' }, couleurfaces: { defaut: '#895632' } }
  } else {
    if ((typeof (objet.mef.couleursommets) === 'undefined') && (typeof (objet.mef.couleurfaces) === 'undefined')) {
      this.mef = { couleursommets: { defaut: 'F00' }, couleurfaces: { defaut: '#895632' } }
    } else if ((typeof (objet.mef.couleursommets) !== 'undefined') && (typeof (objet.mef.couleurfaces) === 'undefined')) {
      this.mef = { couleursommets: objet.mef.couleursommets, couleurfaces: { defaut: '#895632' } }
    } else if ((typeof (objet.mef.couleursommets) === 'undefined') && (typeof (objet.mef.couleurfaces) !== 'undefined')) {
      this.mef = { couleursommets: { defaut: 'F00' }, couleurfaces: objet.mef.couleursommets }
    } else {
      this.mef = objet.mef
    }
  }

  if (typeof (objet.objets) === 'undefined') {
    this.objets = []
  } else {
    this.objets = objet.objets
  }

  // remplissage du tableau objets avec les points sommets du solide
  const tab = []

  for (let k = 1; k < this.sommets.length; k++) {
    const o = {}
    o.estsommet = true// non construit en tant que point car déjà construit
    o.nom = this.nomsommets[k]
    o.type = 'point'
    o.coord = this.sommets[k]
    tab.push(o)
  }
  for (let k = 0; k < this.objets.length; k++) tab.push(this.objets[k])

  this.objets = tab
  let d
  let sup = 0

  for (let k = 1; k < this.sommets.length; k++) {
    d = this.sommets[k][0] * this.sommets[k][0] + this.sommets[k][1] * this.sommets[k][1] + this.sommets[k][2] * this.sommets[k][2]
    if (d > sup) {
      sup = d
    }
  }
  this.zoom = 100 / Math.sqrt(sup)

  // environ 50 dans le cas du cube

  this.conteneur = this.idconteneur

  this.rotation = { Ox: 0, Oy: 0, Oz: 0 }

  // this.svg = j3pCreeSVG(this.idconteneur,{id:this.idconteneur+"figure",width:400,height:400})
  this.centreX = 150
  this.centreY = 150
  this.SVGfaces = []

  this.sommets2D = []

  this.construit()

  this.mode = 'rien' // "rotation"

  const that = this
  j3pElement(this.conteneur).tabIndex = '1'
  $('#' + this.conteneur).keypress(function (event) {
    if (event.keyCode != 38 && event.keyCode != 40) {
      return
    }

    switch (event.keyCode) {
      case 38:
        that.zoom -= 2
        break
      case 40:
        that.zoom += 2
        break
    }
    that.construit()
  })

  j3pElement(this.conteneur).addEventListener('mousemove',
    function (event) {
      if (that.mode == 'rien') {
        return
      }
      const { x, y } = j3pElement(that.conteneur).getBoundingClientRect()
      that.courantX = event.clientX - x
      that.courantY = event.clientY - y
      that.rotation.Oz = Math.ceil((that.courantX - that.ancienX))
      that.rotation.Oy = Math.ceil((that.courantY - that.ancienY))
      that.construit()
      that.ancienX = that.courantX
      that.ancienY = that.courantY
    }
  )
  j3pElement(this.conteneur).addEventListener('mousedown',
    function (event) {
      that.mode = 'rotation'
      const { x, y } = j3pElement(that.conteneur).getBoundingClientRect()
      that.ancienX = event.clientX - x
      that.ancienY = event.clientY - y
    }
  )
  j3pElement(this.conteneur).addEventListener('mouseup',
    function (event) {
      that.mode = 'rien'
    }
  )
}

// tab : coord 3D
// retourne les coord de tab dans le repere designe par this.repere
Solide.prototype.coordDansRepere = function (tab) {
  const O = this.objets[this.repere[0] - 1].coord
  const I = this.objets[this.repere[1] - 1].coord
  const J = this.objets[this.repere[2] - 1].coord
  const K = this.objets[this.repere[3] - 1].coord

  const vecOI = [I[0] - O[0], I[1] - O[1], I[2] - O[2]]
  const vecOJ = [J[0] - O[0], J[1] - O[1], J[2] - O[2]]
  const vecOK = [K[0] - O[0], K[1] - O[1], K[2] - O[2]]
  const vecOM = [tab[0] - O[0], tab[1] - O[1], tab[2] - O[2]]

  const m = [[vecOI[0], vecOJ[0], vecOK[0]], [vecOI[1], vecOJ[1], vecOK[1]], [vecOI[2], vecOJ[2], vecOK[2]]]
  const m1 = j3pInverseMatrice(m)
  return j3pProduitMatVec(m1, vecOM)
}

Solide.prototype.estDansParallelepipedeRepere = function (tab) {
  const res = this.coordDansRepere(tab)
  return (res[0] < 1 + 0.05 && res[0] > -0.05) && (res[1] < 1 + 0.05 && res[1] > -0.05) && (res[2] < 1 + 0.05 && res[2] > -0.05)
}

Solide.prototype.to2D = function (tab) {
  // car vision suivant Oi
  return [tab[1] * this.zoom + this.centreX, -tab[2] * this.zoom + this.centreY]
}

Solide.prototype.construit = function () {
  if (typeof (this.svg) !== 'undefined') {
    j3pDetruit(this.idconteneur + 'figure')
  }

  this.svg = j3pCreeSVG(this.idconteneur, { id: this.idconteneur + 'figure', width: 300, height: 300 })
  const newsommets = []
  let tempo = []
  for (let k = 0; k < this.sommets.length; k++) {
    tempo = j3pRotation('Ox', this.rotation.Ox, this.sommets[k])
    tempo = j3pRotation('Oy', this.rotation.Oy, tempo)
    newsommets.push(j3pRotation('Oz', this.rotation.Oz, tempo))
  }

  for (let k = 0; k < this.faces.length; k++) {
    const f = this.faces[k]
    const s = newsommets
    const vec1 = [s[f[1]][0] - s[f[0]][0], s[f[1]][1] - s[f[0]][1], s[f[1]][2] - s[f[0]][2]]
    const vec2 = [s[f[2]][0] - s[f[1]][0], s[f[2]][1] - s[f[1]][1], s[f[2]][2] - s[f[1]][2]]
    // console.log(vec1,vec2)
    // composante Ox du vecteur normal à la face
    // Si négative alors face cachée@
    const pv = j3pVectoriel(vec1, vec2)[0]
    // console.log('pv :'+k," "+pv)

    for (let u = 0; u < f.length; u++) {
      const v = (u == f.length - 1) ? 0 : u + 1
      if (pv > 0) {
        j3pCreeSegment(this.svg, {
          id: 'face' + k + '_' + u,
          x1: this.to2D(s[f[u]])[0],
          y1: this.to2D(s[f[u]])[1],
          x2: this.to2D(s[f[v]])[0],
          y2: this.to2D(s[f[v]])[1],
          couleur: '#000000',
          epaisseur: 3,
          opacite: 1
        })
      } else {
        j3pCreeSegment(this.svg, {
          id: 'face' + k + '_' + u,
          x1: this.to2D(s[f[u]])[0],
          y1: this.to2D(s[f[u]])[1],
          x2: this.to2D(s[f[v]])[0],
          y2: this.to2D(s[f[v]])[1],
          couleur: '#222222',
          epaisseur: 2,
          opacite: 1,
          pointilles: '2,6'
        })
      }
    }

    // console.log("vuecache",f,pv)
    if (pv > 0) {
      const opacite = 0.5
      let tab = []
      if (f.length == 4) {
        tab = [this.to2D(s[f[0]]), this.to2D(s[f[1]]), this.to2D(s[f[2]]), this.to2D(s[f[3]])]
      }
      if (f.length == 3) {
        tab = [this.to2D(s[f[0]]), this.to2D(s[f[1]]), this.to2D(s[f[2]])]
      }

      let couleurface = this.mef.couleurfaces.defaut
      if (typeof (this.mef.couleurfaces['f' + k]) !== 'undefined') {
        couleurface = this.mef.couleurfaces['f' + k]
      }

      j3pPolygone(this.svg, {
        id: 'face' + k,
        tab,
        couleur: couleurface,
        couleurRemplissage: couleurface,
        opaciteRemplissage: opacite
      })
    }
  }

  let nom
  for (let k = 1; k < this.nomsommets.length; k++) {
    nom = this.nomsommets[k]
    this.objets[this.numeroObjet(nom)].coord = this.sommets[k]

    if (nom != 'som' + k) {
      const coord = this.sommets[k]
      const coord2D = this.to2D(coord)
      let couleursommet = this.mef.couleursommets.defaut
      if (typeof (this.mef.couleursommets[nom]) !== 'undefined') {
        couleursommet = this.mef.couleursommets[nom]
      }

      j3pCreeTexte(this.svg, {
        id: 'nomsommet' + nom,
        texte: nom,
        x: coord2D[0],
        y: coord2D[1],
        couleur: couleursommet,
        italique: true,
        taille: 12
      })
    }
  }

  this.sommets = newsommets

  // Construction des objets particuliers
  /*
   objets : [
                  {nom:"O",type:"point",coord:[0,0,0]},
                  {nom:"s1",type:"segment",ext1:"O",ext2:"K"}
              ]
  */

  for (let k = 0; k < this.objets.length; k++) {
    const o = this.objets[k]
    switch (o.type) {
      case 'point':
        if (typeof (o.estsommet) === 'undefined') {
          let tempo = rotation3D('Ox', this.rotation.Ox, o.coord)
          tempo = rotation3D('Oy', this.rotation.Oy, tempo)
          tempo = rotation3D('Oz', this.rotation.Oz, tempo)
          const coord2D = this.to2D(tempo)
          // j3pCreePoint(this.svg,{id:this.idconteneur+"point"+o.nom,nom:o.nom,x:coord2D[0],y:coord2D[1],taille:12,couleur:"#00F",taillepolice:14,epaisseur:2,decal:[-5,-5]});
          j3pBoule(this.svg, {
            couleur: 'rouge',
            id: this.idconteneur + 'point' + o.nom,
            cx: coord2D[0],
            cy: coord2D[1],
            diametre: 10
          })
          j3pCreeTexte(this.svg, {
            id: this.idconteneur + 'textepoint' + o.nom + nom,
            texte: o.nom,
            x: coord2D[0] - 8,
            y: coord2D[1] - 7,
            couleur: '#F00',
            italique: true,
            taille: 12
          })
          this.objets[k].coord = tempo
        }
        break

      case 'segment': {
        const coordext1 = this.objets[this.numeroObjet(o.ext1)].coord
        const coordext2 = this.objets[this.numeroObjet(o.ext2)].coord
        const coord2D1 = this.to2D(coordext1)
        const coord2D2 = this.to2D(coordext2)
        const bool1 = this.estDansParallelepipedeRepere(coordext1)
        const bool2 = this.estDansParallelepipedeRepere(coordext2)

        if (bool1 && bool2) {
          j3pCreeSegment(this.svg, {
            id: this.idconteneur + 'segment' + nom,
            x1: coord2D1[0],
            y1: coord2D1[1],
            x2: coord2D2[0],
            y2: coord2D2[1],
            couleur: '#00AA44',
            epaisseur: 3,
            opacite: 1,
            pointilles: '4,6'
          })
        } else {
          j3pCreeSegment(this.svg, {
            id: this.idconteneur + 'segment' + nom,
            x1: coord2D1[0],
            y1: coord2D1[1],
            x2: coord2D2[0],
            y2: coord2D2[1],
            couleur: '#00AA44',
            epaisseur: 3,
            opacite: 1
          })
        }
        break
      } // case segment
    }
  }
}

Solide.prototype.tourne = function (coord) {
  let tempo = rotation3D('Ox', this.rotation.Ox, coord)
  tempo = rotation3D('Oy', this.rotation.Oy, tempo)
  tempo = rotation3D('Oz', this.rotation.Oz, tempo)
  return tempo
}

Solide.prototype.numeroObjet = function (nom) {
  for (let k = 0; k < this.objets.length; k++) {
    if (this.objets[k].nom == nom) {
      return k
    }
  }
  return -1
}

/**
 * Rotation /t ch du point tab et d’angle angle
 * @private
 */
function rotation3D (ch, angle, tab) {
  angle = (angle / 180) * Math.PI
  const res = []
  switch (ch) {
    case 'Ox':
      res[0] = tab[0]
      res[1] = tab[1] * Math.cos(angle) - tab[2] * Math.sin(angle)
      res[2] = tab[1] * Math.sin(angle) + tab[2] * Math.cos(angle)
      break
    case 'Oy':
      res[0] = tab[2] * Math.sin(angle) + tab[0] * Math.cos(angle)
      res[1] = tab[1]
      res[2] = tab[2] * Math.cos(angle) - tab[0] * Math.sin(angle)
      break
    case 'Oz':
      res[0] = tab[0] * Math.cos(angle) - tab[1] * Math.sin(angle)
      res[1] = tab[0] * Math.sin(angle) + tab[1] * Math.cos(angle)
      res[2] = tab[2]
      break
  }
  return res
}
/* */