modules/2dinteractif.js

import { point, polygone, tracePoint } from './2d.js'
import { colorToLatexOrHTML, ObjetMathalea2D } from './2dGeneralites.js'
import { context } from './context.js'

/**
 * @author Rémi ANGOT
 * @param {number} x abscisse du point
 * @param {number} y ordonnée du point
 * @param {object} options over, out et click sont des objets pour le style css des évènements de la souris, radius, width, color, opacite, size, style sont les paramètres possibles pour la trace du point
 */
function PointCliquable (x, y, options) {
  ObjetMathalea2D.call(this, { })
  const A = point(x, y)
  this.point = A
  if (!options) options = {}
  const out = options.out || { opacity: 0 }
  const over = options.over || { opacity: 0.5 }
  const click = options.click || { opacity: options.opacite || 1 }
  this.etat = false // Pour récupérer si le point est affiché ou pas
  this.svg = function (coeff) {
    let code
    const trace = tracePoint(A, options.color || options.couleur || 'black')
    trace.epaisseur = options.width || options.epaisseur || 1
    trace.taille = options.size || options.taille || 3
    trace.isVisible = false
    trace.style = options.style || 'x'
    code = `<g id="${this.id}">\n`
    code += trace.svg(coeff) + '\n'
    // Le cercle est la zone d'effet pour les évènements
    // Comme fill est à none, il faut ajouter pointer-events="visible" cf https://www.smashingmagazine.com/2018/05/svg-interaction-pointer-events-property/
    code += `<circle cx="${A.xSVG(coeff)}" cy="${A.ySVG(coeff)}" r="${(options.radius || options.rayon || 1) * coeff}" fill="none" pointer-events="visible" />\n`
    code += '</g>'
    return code
  }
  const moi = this // Pour utiliser this dans les fonctions
  const gestionDeLaSouris = () => {
    document.removeEventListener('exercicesAffiches', gestionDeLaSouris)
    const groupe = document.getElementById(`${this.id}`)
    const changeEtatPoint = (etat) => {
      this.etat = etat
    }
    // On initialise avec le style de out
    if (groupe) {
      for (const key in out) {
        groupe.style[key] = out[key]
      }
      groupe.addEventListener('mouseover', mouseOverEffect)
      groupe.addEventListener('mouseout', mouseOutEffect)
      groupe.addEventListener('click', mouseClick)
      function mouseOverEffect () {
        for (const key in over) {
          this.style[key] = over[key]
        }
      }
      function mouseOutEffect () {
        for (const key in out) {
          this.style[key] = out[key]
        }
      }
      function mouseClick () {
        if (moi.etat) {
          // On désactive le point
          groupe.addEventListener('mouseover', mouseOverEffect)
          groupe.addEventListener('mouseout', mouseOutEffect)
          // On lui remet le style de out
          for (const key in out) {
            this.style[key] = out[key]
          }
          moi.etat = false
          changeEtatPoint(false)
        } else {
          // On désactive les listeners
          groupe.removeEventListener('mouseover', mouseOverEffect)
          groupe.removeEventListener('mouseout', mouseOutEffect)
          // On applique le style de click
          for (const key in click) {
            this.style[key] = click[key]
          }
          moi.etat = true
        }
      }
    }
  }
  document.addEventListener('exercicesAffiches', gestionDeLaSouris)
  this.stopCliquable = () => {
    const groupe = document.getElementById(`${this.id}`)
    // On retire tous les listener en le remplaçant par un clone
    groupe.replaceWith(groupe.cloneNode(true))
  }
}

export function pointCliquable (...args) {
  return new PointCliquable(...args)
}

/**
 * @author Rémi ANGOT
 * @param {number} x abscisse du point
 * @param {number} y ordonnée du point
 * @param {object} options over, out et click sont des ojets pour le style css des évènements de la souris, radius, width, color, size, style sont les paramètres possibles pour la trace du point
 */
function RectangleCliquable (x1, y1, x2, y2, options) {
  ObjetMathalea2D.call(this, { })
  const A = point(x1, y1)
  const B = point(x2, y1)
  const C = point(x2, y2)
  const D = point(x1, y2)
  const rectangle = polygone(A, B, C, D)
  const bordure = polygone(A, B, C, D)
  if (!options) options = {}
  const out = options.out || { opacity: 0 }
  const over = options.over || { opacity: 0.2 }
  const click = options.click || { opacity: 1 }
  const couleur = options.couleur || '#f15929'
  const cliquable = (options.cliquable !== undefined) ? options.cliquable : true
  rectangle.hachures = options.hachures || false
  rectangle.epaisseurDesHachures = options.epaisseurDesHachures || 4
  bordure.epaisseur = options.epaisseur || 1
  this.etat = options.etat || false // Pour récupérer si le rectangle est cliqué ou pas
  this.svg = function (coeff) {
    let code
    rectangle.couleurDeRemplissage = colorToLatexOrHTML(options.color || options.couleur || options.couleurDeRemplissage || '#f15929')
    rectangle.epaisseur = 0
    rectangle.isVisible = false
    code = `<g id="rectangle${this.id}">\n`
    code += rectangle.svg(coeff) + '\n'
    code += '</g>'
    code += bordure.svg(coeff)
    return code
  }
  this.tikz = (coeff) => {
    if (this.etat) bordure.couleurDeRemplissage = colorToLatexOrHTML(couleur)
    bordure.hachures = rectangle.hachures
    return bordure.tikz(coeff)
  }
  const moi = this // Pour utiliser this dans les fonctions
  const gestionDeLaSouris = () => {
    document.removeEventListener('exercicesAffiches', gestionDeLaSouris)
    const groupe = document.getElementById('rectangle' + this.id)
    const changeEtatPoint = (etat) => {
      this.etat = etat
    }
    if (groupe) {
      // On initialise avec le style de out ou de click suivant l'état
      for (const key in out) {
        groupe.style[key] = (this.etat) ? click[key] : out[key]
      }
    }
    if (groupe && cliquable) {
      groupe.addEventListener('mouseover', mouseOverEffect)
      groupe.addEventListener('mouseout', mouseOutEffect)
      groupe.addEventListener('click', mouseClick)
      function mouseOverEffect () {
        for (const key in over) {
          this.style[key] = over[key]
        }
      }
      function mouseOutEffect () {
        for (const key in out) {
          this.style[key] = out[key]
        }
      }
      function mouseClick () {
        if (moi.etat) {
          // On désactive le point
          groupe.addEventListener('mouseover', mouseOverEffect)
          groupe.addEventListener('mouseout', mouseOutEffect)
          // On lui remet le style de out
          for (const key in out) {
            this.style[key] = out[key]
          }
          moi.etat = false
          changeEtatPoint(false)
        } else {
          // On désactive les listeners
          groupe.removeEventListener('mouseover', mouseOverEffect)
          groupe.removeEventListener('mouseout', mouseOutEffect)
          // On applique le style de click
          for (const key in click) {
            this.style[key] = click[key]
          }
          moi.etat = true
        }
      }
    }
  }
  document.addEventListener('exercicesAffiches', gestionDeLaSouris)
  this.stopCliquable = () => {
    const groupe = document.getElementById(`${this.id}`)
    // On retire tous les listener en le remplaçant par un clone
    groupe.replaceWith(groupe.cloneNode(true))
  }
}

export function rectangleCliquable (...args) {
  return new RectangleCliquable(...args)
}

export function fractionCliquable (x, y, unites, denominateur, options) {
  const objets = []
  if (!options) options = {}
  const longueur = options.longueur || 4
  const ecart = options.ecart || 1
  const hauteur = options.hauteur || 1
  const liste1 = options.liste1 || []
  const liste2 = options.liste2 || []
  let couleur1 = options.couleur1 || '#f15929'
  let couleur2 = options.couleur2 || '#1DA962'
  if (!context.isHtml) {
    couleur1 = options.couleur1 || 'gray'
    couleur2 = options.couleur2 || 'lightgray'
  }
  const hachures1 = options.hachures1 || false
  const hachures2 = options.hachures2 || false
  const couleur = options.couleur || couleur1
  const cliquable = (options.cliquable !== undefined) ? options.cliquable : true
  let O
  for (let i = 0; i < unites; i++) {
    O = point(x + i * (longueur + ecart), y)
    for (let j = 0; j < denominateur; j++) {
      if (liste1.includes(i * denominateur + j + 1)) {
        objets.push(rectangleCliquable(O.x + j * longueur / denominateur, y, O.x + (j + 1) * longueur / denominateur, y + hauteur,
          { cliquable, etat: true, couleur: couleur1, hachures: hachures1 }))
      } else if (liste2.includes(i * denominateur + j + 1)) {
        objets.push(rectangleCliquable(O.x + j * longueur / denominateur, y, O.x + (j + 1) * longueur / denominateur, y + hauteur,
          { cliquable, etat: true, couleur: couleur2, hachures: hachures2 }))
      } else {
        objets.push(rectangleCliquable(O.x + j * longueur / denominateur, y, O.x + (j + 1) * longueur / denominateur, y + hauteur, { cliquable, couleur, etat: false }))
      }
    }
  }
  return objets
}