legacy/outils/geometrie/Repere.js

import { j3pAddElt, j3pArrondi, j3pC, j3pDistance, j3pElement, j3pEmpty, j3pGetNewId, j3pImageRotation, j3pIntersection, j3pIsHtmlElement, j3pNotify, j3pPointDansSegment } from 'src/legacy/core/functions'
import { j3pEquation } from 'src/legacy/outils/geometrie/functions'
import { j3pCreeAngle, j3pCreeArc, j3pCreeCercle, j3pCreeRectangle, j3pCreeSecteur, j3pCreeSegment, j3pCreeSVG, j3pCreeTexte, j3pGradueSegment, j3pPolygone } from 'src/legacy/core/functionsSvg'
import { j3pTableauValeurs } from 'src/legacy/core/functionsTarbre'

/**
 * Petit moteur de géométrie dynamique
 * @fileOverview
 * @author JP Vanroyen
 * @since Novembre 2012
 *
 * Exemple de syntaxe d’appel
 ```
 var repere = new Repere({
  idConteneur: this.zones.MG,
  idDivRepere: 'unrepere',
  aimantage: false,
  visible: true,
  larg: 500,
  haut: 500,
  pasdunegraduationX: 1,
  pixelspargraduationX: 30,
  pasdunegraduationY: 1,
  pixelspargraduationY: 30,
  xO: 250,
  yO: 250,
  debuty: 0,
  negatifs: true,
  objets: [{
    type: 'point',
    nom: 'A',
    par1: 6,
    par2: 4,
    fixe: false,
    visible: true,
    etiquette: true,
    style: {couleur: '#00F', epaisseur: 2, taille: 18}
  }, {
    type: 'point',
    nom: 'B',
    par1: 4,
    par2: -3,
    fixe: false,
    visible: true,
    etiquette: true,
    style: {couleur: '#00F', epaisseur: 2, taille: 18}
  }, {
    type: 'point',
    nom: 'C',
    par1: -3,
    par2: -6,
    fixe: false,
    visible: true,
    etiquette: true,
    style: {couleur: '#0F0', epaisseur: 2, taille: 18}
  },
  {type: 'segment', nom: 's', par1: 'A', par2: 'B', style: {couleur: '#456', epaisseur: 2}},
  {type: 'cercle', nom: 'ce', par1: 'A', par2: 'B', style: {couleur: '#456', epaisseur: 2}},
  {type: 'droite', nom: 'd1', par1: 'A', par2: 'C', style: {couleur: '#382', epaisseur: 2}},
  {type: 'droiteparallele', nom: 'd2', par1: 'B', par2: 'd1', style: {couleur: '#538', epaisseur: 2}},
  {type: 'droiteperpendiculaire', nom: 'd3', par1: 'B', par2: 'd1', style: {couleur: '#F00', epaisseur: 2}},
  {
    type: 'point',
    nom: 'D',
    par1: -1,
    par2: 5,
    fixe: false,
    visible: true,
    etiquette: true,
    style: {couleur: '#000', epaisseur: 2, taille: 18}
  }, {
    type: 'point',
    nom: 'E',
    par1: -2,
    par2: 3,
    fixe: false,
    visible: true,
    etiquette: true,
    style: {couleur: '#000', epaisseur: 2, taille: 18}
  },
  {type: 'droite', nom: 'd4', par1: 'D', par2: 'E', style: {couleur: '#FA5', epaisseur: 2}},
  {
    type: 'pointintersection',
    nom: 'T',
    par1: 'd4',
    par2: 'd3',
    visible: true,
    etiquette: true,
    style: {couleur: '#0F0', epaisseur: 2, taille: 18}
  }, {
    type: 'pointintersection',
    nom: 'S',
    par1: 'd4',
    par2: 'd2',
    visible: true,
    etiquette: true,
    style: {couleur: '#0F0', epaisseur: 2, taille: 18}
  },
  {type: 'droite', nom: 'd5', par1: 'T', par2: 'A', style: {couleur: '#FA5', epaisseur: 2}},
  {type: 'courbe', nom: 'c1', par1: '0.3*x^2-2', par2: -5, par3: 5, par4: 30, style: {couleur: '#FA5', epaisseur: 2}},
  {
    type: 'courbe_tabvaleur',
    nom: 'c1',
    tab_val: [[0, 1], [0.1, 1.2], [0.2, 1.5]],
    style: {couleur: '#FA5', epaisseur: 2}
  }]
})
repere.construit()
```
 */

/**
 * Position ou coordonnées (l’unité peut être le pixel ou l’unité du repère suivant le contexte)
 * @typedef XY
 * @type Object
 * @property {number} x
 * @property {number} y
 */

/**
 * Retourne la chaine nettoyée de ses caractères non alphanumériques (pour pouvoir être utilisé comme id)
 * @param {string} chaine
 * @return {string}
 */
export function cleanForId (chaine) {
  // \W revient au même que [^\w] soit [^A-Za-z0-9_]
  if (typeof chaine === 'string') return chaine.replace(/\W/g, '')
  console.error(Error('Il faut passer une string'), typeof chaine)
  return ''
}

const identity = arg => arg

/**
 * Retourne le coef directeur de la droite
 * @private
 * @param {Repere} repere
 * @param {string} par1 Nom du point 1 dans repere.objets
 * @param {string} par2 Nom du point 2 dans repere.objets
 * @return {number} Coef directeur (Number.POSITIVE_INFINITY si verticale)
 */
function coefdir (repere, par1, par2) {
  const o1 = repere.getObjet(par1)
  const o2 = repere.getObjet(par2)
  const px1 = o1.par1
  const py1 = o1.par2
  const px2 = o2.par1
  const py2 = o2.par2
  if (px1 === px2) return Number.POSITIVE_INFINITY
  return (py2 - py1) / (px2 - px1)
}

/**
 * Helper de construit pour ajouter les objets dans le repère
 * @private
 * @param {Repere} repere
 */
function construitObjets (repere) {
  // au cas ou des points seraient déclarés après des objets basés dessus, on leur affecte leurs props x & y d’abord
  for (const objet of repere.objets) {
    if (objet.type === 'point') {
      objet.x = objet.par1
      objet.y = objet.par2
    }
  }

  for (const [k, objet] of repere.objets.entries()) {
    if (!repere.objetsByNom.has(objet.nom)) {
      console.warn(`À la reconstruction on a un objet ${objet.nom} dans objets qui n’est pas dans objetsByNom`)
      repere.add(objet)
    }
    switch (objet.type) {
      case 'point':
        // FIXME pourquoi tester visible ? la méthode point le gère…
        if (objet.visible) repere.point(objet)
        break
      case 'segment' :
        repere.segment(objet)
        break
      case 'droite' :
        repere.droite(objet)
        break
      case 'demidroite' :
        repere.demidroite(objet)
        break
      case 'droiteparallele' :
        repere.droiteparallele(objet)
        break
      case 'droiteperpendiculaire' :
        repere.droiteperpendiculaire(k, objet.nom, objet.par1, objet.par2, objet.style)
        break
      case 'pointintersection' :
        repere.pointintersection(objet.nom, objet.par1, objet.par2, true, objet.etiquette, objet.style)
        break

      // {type:'cercle',nom:'ce',par1:'A',par2:'B',style:{couleur:'#FA5',couleurRemplissage:'#FA5',epaisseur:2,opaciteRemplissage:0.3}},
      case 'cercle' :
        repere.cercle(objet.nom, objet.par1, objet.par2, objet.style)
        break
      case 'angle' :
        repere.angle(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
        break
      case 'cercledia' :
        repere.cercledia(objet.nom, objet.par1, objet.par2, objet.style)
        break
      // {type:'courbe',nom:'c1',par1:'0.3*x^2-2',par2:-5,par3:5,par4:30,style:{couleur:'#FA5',epaisseur:2}}
      case 'courbe':
        repere.courbe(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
        break
      // {type:'courbe_tabvaleur',nom:'c1',tab_val:[[0,1], [0.1,1.2], [0.2,1.5], ...] ,style:{couleur:'#FA5',epaisseur:2}}
      case 'courbe_tabvaleur':
        repere.courbe_tabvaleur(objet.nom, objet.tab_val, objet.style)
        break
      // {type:'prisme',nom:'pr1',par1:'A',par2:2,par3:4,par4:45,style:{couleur:'#FA5',couleurRemplissage:'#FA5',epaisseur:2,opaciteRemplissage:0.3}}
      case 'prisme':
        repere.prisme(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.par5, objet.style)
        break
      case 'polygone':
        repere.polygone(objet.nom, objet.par1, objet.style)
        break
      case 'pointsur':
        repere.pointsur(objet)
        break
      case 'secteur':
        repere.secteur(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
        break
      case 'ellipse':
        repere.ellipse(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
        break
      case 'ellipse2':
        repere.ellipse2(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.par5, objet.par6, objet.par7, objet.par8, objet.par9, objet.style)
        break
      case 'ellipse3' :
        repere.ellipse3(objet.nom, objet.cx, objet.cy, objet.rx, objet.ry, objet.angledeb, objet.anglefin, objet.rotation, objet.style)
        break
      case 'face':
        repere.face(objet.nom, objet.par1, objet.style)
        break
      case 'arc': {
        const centre = repere.getObjet(objet.centre || objet.par1)
        if (!centre) return console.error('pas trouvé le centre de nom ' + objet.par1)
        const posCentre = repere.RenvoiePixels(centre.x, centre.y)
        const rayon = Number(objet.rayon || objet.par2) * repere.pixelspargraduationX
        const svgOptions = objet.style
        const arcOptions = {
          centreX: posCentre[0],
          centreY: posCentre[1],
          rayon,
          angleDebut: Number(objet.angleDebut || objet.par3),
          angle: Number(objet.angle || objet.par4)
        }
        if (objet.nom) arcOptions.id = objet.nom
        // et on garde dans l’objet une ref sur l’élément svg créé
        objet.svg = j3pCreeArc(repere.svg, svgOptions, arcOptions)
        break
      }

      default:
        // les noms préfixés par $ n’ont apparemment pas de type et c’est normal
        if (!objet.nom || objet.nom[0] !== '$') console.error(Error('type inconnu pour cet objet'), objet)
    }
  }
} // construitObjets

/**
 * Crée un path avec objet.d
 * @private
 * @param svg
 * @param objet
 * @return {SVGPathElement}
 */
function creeFace (svg, objet) {
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
  path.setAttribute('id', objet.id)
  path.setAttribute('d', objet.d)
  path.setAttribute('style', 'stroke:' + objet.couleur + ';fill:' + objet.couleurRemplissage + ';stroke-width:0;fill-opacity:' + objet.opaciteRemplissage + ';stroke-dasharray:' + objet.pointilles)
  if (typeof svg === 'string') svg = j3pElement(svg)
  svg.appendChild(path)
  return path
}

/**
 * @private
 * @param {string|SVGElement} svg
 * @param objet
 * @return {*}
 */
function creeEllipse2 (svg, objet) {
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
  path.setAttribute('id', objet.id)
  const X0 = objet.centreX
  const Y0 = 300 - objet.centreY
  const X1 = objet.ArriveX
  const Y1 = objet.ArriveY
  const Rx = objet.rx
  const Ry = objet.ry
  const c8 = objet.p8
  const c9 = objet.p9

  let ch = 'M ' + X0 + ',' + Y0 + ' '
  ch += ' a ' + Rx + ' ' + Ry + ' ' + 0 + ' ' + c8 + ' ' + c9 + ' ' + X1 + ',' + Y1

  path.setAttribute('d', ch)
  path.setAttribute('style', 'stroke:' + objet.couleur + ';fill:' + objet.couleurIn + ';stroke-width:' + objet.epaisseur + ';fill-opacity:' + objet.opacite + ';stroke-dasharray:' + objet.pointilles)
  if (typeof (svg) === 'string') svg = j3pElement(svg)
  svg.appendChild(path)
  return path
}

/**
 * @private
 * @param {string|SVGElement} svg
 * @param objet
 */
function creeEllipse3 (svg, objet) {
  function ladist (p1, p2) {
    return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))
  }

  let lim1 = 9999999999
  let lim2 = 9999999999
  if (objet.style.pointilles) {
    lim1 = parseFloat(objet.style.pointilles.substring(0, objet.style.pointilles.indexOf(',')))
    lim2 = parseFloat(objet.style.pointilles.substring(objet.style.pointilles.indexOf(',') + 1))
  }
  let tour = true
  let pointref = {
    x: objet.centreX + objet.rx * Math.cos(objet.angledeb),
    y: objet.centreY - objet.ry * Math.sin(objet.angledeb)
  }
  for (let i = objet.angledeb; i < objet.anglefin; i++) {
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
    path.setAttribute('id', objet.id + i)
    const angle1 = (i / 180) * Math.PI
    const angle2 = (i + 1) / 180 * Math.PI
    const X0 = objet.centreX + objet.rx * Math.cos(angle1)
    const Y0 = objet.centreY - objet.ry * Math.sin(angle1)
    const X1 = objet.centreX + objet.rx * Math.cos(angle2)
    const Y1 = objet.centreY - objet.ry * Math.sin(angle2)
    const pointar = { x: X1, y: Y1 }
    const unedsit = ladist(pointref, pointar)
    if (tour) {
      if (unedsit > lim1) {
        tour = false
        pointref = pointar
      }
    } else {
      if (unedsit > lim2) {
        tour = true
        pointref = pointar
      }
    }

    if (tour) {
      let ch = 'M ' + X0 + ' ' + Y0 + ' '
      ch += ' A ' + objet.rx + ' ' + objet.ry + ' ' + ' 0  0 ,0 ' + X1 + ' ' + Y1// "M "+X0+" "+Y0+
      path.setAttribute('d', ch)
      path.setAttribute('style', 'stroke:' + objet.style.couleur + ';fill:none;stroke-width:2;fill-opacity:' + objet.style.opacite)
      if (typeof svg === 'string') svg = j3pElement(svg)
      svg.appendChild(path)
    }
  }
}

/**
 * Retourne un nombre de 12 à 35 suivant la longueur de ch
 * pour decalage à gauche par rapport à (0y) ???
 * @param {string} ch
 * @return {number}
 * @private
 */
function decalage (ch) {
  switch (ch.length) {
    case 1:
      return 12
    case 2:
      return 17
    case 3:
      return 22
    case 4:
      return 25
    case 5:
      return 30
    default :
      return 35
  }
}

/**
 * Retourne la position de la souris relative au svg (en pixels, même si la souris est hors du svg)
 * @private
 * @param {Repere} repere
 * @param {MouseEvent} event
 * @return {XY}
 */
function getPosRelSouris (repere, event) {
  // les deux sont relatifs au viewport
  const { clientX, clientY } = event
  const { x, y } = repere.svg.getBoundingClientRect()
  return {
    x: clientX - x,
    y: clientY - y
  }
}

/**
 * Retourne un vecteur de même direction et de norme 1
 * @param {number[]} vect
 * @return {number[]}
 * @private
 */
function normalise (vect) {
  const x = vect[0]
  const y = vect[1]
  const norme = Math.sqrt(x * x + y * y)
  return [x / norme, y / norme]
}

/**
 * Appelé sur le mouseMove (avec un bind !), une catastrophe car on détruit et reconstruit tout dès qu’un point est sélectionné !
 * @param event
 * @this Repere
 * @private
 */
function onMove (event) {
  const repere = this
  if (!this.hasMouseDown) return

  const { x, y } = getPosRelSouris(repere, event)
  if (!repere.pointselectionne) {
    // si y’a pas de point sélectionné, que le clic est enfoncé et que le repère est mobile faut le déplacer
    if (!repere.fixe) {
      repere.xO += x - repere._lastClickPos.x
      repere.yO += y - repere._lastClickPos.y
      repere._lastClickPos = { x, y }
      repere.construit()
    }
    // rien à faire d’autre
    return
  }

  // y’a un point sélectionné => on va détruire et recréer le svg à chaque fois que la souris bouge d’un pixel
  // pourquoi tant de haine ?

  // on passe en revue les points imposés dans tout repère
  if (repere.pointselectionne === 'O') {
    if (!repere.fixe) {
      repere.xO = x
      repere.yO = y
      repere.construit()
    }
    return
  }

  if (repere.pointselectionne === '$I') {
    repere.pixelspargraduationX = x - repere.xO
    if (repere.pixelspargraduationX > 10) repere.construit()
    return
  }

  if (repere.pointselectionne === '$J') {
    repere.pixelspargraduationY = -y + repere.yO
    if (repere.pixelspargraduationY > 10) repere.construit()
    return
  }

  if (repere.pointselectionne === '$K') {
    repere.pixelspargraduationX = Math.floor((event.pageX - j3pC().x - repere.xO) / 10)
    if (repere.pixelspargraduationX > 10) repere.construit()
    return
  }

  if (repere.pointselectionne === '$L') {
    repere.pixelspargraduationY = Math.floor((-event.pageY + j3pC().y + repere.yO) / 10)
    if (repere.pixelspargraduationY > 10) repere.construit()
    return
  }

  // sinon c’est un autre point sélectionné
  const objSelect = repere.getObjet(repere.pointselectionne)
  if (objSelect.fixe) return
  const cs = pos2coord(repere, { x, y })
  if (objSelect.type === 'pointsur') {
    // il faut changer le coefficient
    // on recupere les extremites du segment (ou droite)
    const indiceExt1 = objSelect.par1
    const ext1 = repere.getObjet(indiceExt1)
    const indiceExt2 = objSelect.par2 // extremité du segment
    const ext2 = repere.getObjet(indiceExt2)
    const eq = j3pEquation(ext1.x, ext1.y, ext2.x, ext2.y)
    const proj = repere.projeteOrthogonal(eq, [cs.x, cs.y])

    if (ext2.x === ext1.x) {
      objSelect.par2 = (proj[1] - ext1.y) / ((ext2.y - ext1.y))
    } else {
      objSelect.par2 = (proj[0] - ext1.x) / ((ext2.x - ext1.x))
    }
    if (objSelect.type === 'segment') {
      if (objSelect.par2 < 0) objSelect.par2 = 0.01
      if (objSelect.par2 > 1) objSelect.par2 = 0.99
    }
  } else if (repere.aimantage) {
    if ((Math.abs(objSelect.par1 - cs.x) > 0.8) || (Math.abs(objSelect.par2 - cs.y) > 0.8)) {
      objSelect.par1 = Math.round(cs.x)
      objSelect.par2 = Math.round(cs.y)
    } else {
      // ça a bougé un peu mais pas assez pour changer le point de place => rien à faire
      return
    }
  } else {
    objSelect.par1 = cs.x
    objSelect.par2 = cs.y
  }
  repere.construit()
} // onMove

/**
 * Transforme une position en pixel dans le repère en coordonnées
 * @param {Repere} repere
 * @param {XY} pos en pixels
 * @return {XY} les coordonnées (3 décimales)
 * @private
 */
function pos2coord (repere, pos) {
  return {
    x: j3pArrondi(((pos.x - repere.xO) / repere.pixelspargraduationX) * repere.pasdunegraduationX, 3),
    y: j3pArrondi(((-pos.y + repere.yO) / repere.pixelspargraduationY) * repere.pasdunegraduationY, 3)
  }
}

/**
 * Constructeur d’un repère en svg
 * @param {Object} props Les valeurs du repère
 * @param {string|HTMLElement} props.idConteneur
 * @param {string} [props.idDivRepere]
 * @param {string} [props.indice=''] suffixe qui sera utilisé sur tous les id générés
 * @param {string} props.pointselectionne
 * @param {number} props.larg
 * @param {number} props.haut
 * @param {number} props.pasdunegraduationX
 * @param {number} props.pixelspargraduationX
 * @param {number} props.pasdunegraduationY
 * @param {number} props.pixelspargraduationY
 * @param {number} props.xO
 * @param {number} props.y0
 * @param {number} props.debuty
 * @param {boolean} props.trame
 * @param {boolean} props.negatifs
 * @param {boolean} props.aimantage
 * @param {boolean} [props.visible=false] passer true pour afficher les axes de coordonnées
 * @param {boolean} props.fixe
 * @param {boolean} [props.keepWeaksIds] passer true pour revenir à l’ancien comportement, qui mettait des ids complètement invalides (mais récupérables avec du getElementById)
 * @param {Object[]} props.objets
 * @constructor
 */
function Repere (props) {
  // @todo virer ça dès que les sections arrêteront d’utiliser keepWeaksIds
  // il faudra rechercher / remplacer dans ce fichier (en regex)
  // this._idCleaner => cleanForId
  // this._getId\((.+)\) => j3pGetNewId($1, true)
  // et vérifier que _weakMode et keepWeaksIds n’existent plus dans le projet
  this._weakMode = Boolean(props.keepWeaksIds)
  if (this._weakMode) {
    this._idCleaner = identity
    this._getId = id => id + this._suffix
  } else {
    this._idCleaner = cleanForId
    this._getId = id => j3pGetNewId(id + this._suffix, true)
  }
  const conteneur = typeof props.idConteneur === 'string' ? j3pElement(props.idConteneur) : props.idConteneur
  if (!j3pIsHtmlElement(conteneur)) throw Error('conteneur invalide')
  /**
   * Conteneur du repere
   * @type {HTMLElement}
   */
  this.conteneur = conteneur
  this._suffix = typeof props.indice === 'string'
    ? props.indice
    : typeof props.indice === 'number'
      ? String(props.indice)
      : ''
  /**
   * id du div construit autour du svg
   * @type {string}
   */
  this.idDivRepere = this._getId(props.idDivRepere || 'divRepere')
  this.idRepere = this._getId('idRepere' + this._suffix)
  this.larg = Number(props.larg) || 300
  this.haut = Number(props.haut) || 300
  // nécessaire si conteneur est lui-même dans un DIV avec propriété css overflow=true
  this.pasdunegraduationX = props.pasdunegraduationX
  this.pixelspargraduationX = props.pixelspargraduationX
  this.pasdunegraduationY = props.pasdunegraduationY
  this.pixelspargraduationY = props.pixelspargraduationY
  this.pointselectionne = ''
  this.debuty = props.debuty
  this.xO = typeof props.xO === 'number' ? props.xO : this.larg / 2
  this.yO = typeof props.yO === 'number' ? props.yO : this.haut / 2
  this.couleur = {
    ptf: '#00F',
    ptm: '#F00'
  }
  this.trame = Boolean(props.trame)
  this.negatifs = props.negatifs
  this.aimantage = props.aimantage
  this.hasMouseDown = false
  /**
   * Position de la souris au dernier clic (en pixels)
   * Ne sert que pour la translation du repère (dans onMove)
   * @private
   * @type {XY}
   */
  this._lastClickPos = {
    x: 0,
    y: 0
  }
  this.visible = Boolean(props.visible)
  this.fixe = Boolean(props.fixe)

  /**
   * La liste des objets du repère, ne pas la modifier directement, passer par `add(objet)` pour ajouter ou `getObjet(nom)` pour récupérer
   * @type {RepereObject[]}
   */
  this.objets = []
  /**
   * Idem objets mais indexés par leur nom
   * @type {Map<string, RepereObject>}
   */
  this.objetsByNom = new Map()
  // on init avec qq points
  // origine
  this.add({ type: 'point', nom: '_O', par1: 0, par2: 0 })
  // unité X
  this.add({
    type: 'point',
    nom: '_I',
    par1: 1,
    par2: 0,
    fixe: false,
    visible: false,
    etiquette: false,
    style: { couleur: '#456', epaisseur: 2, taille: 18, taillepoint: 5 }
  })
  // unité Y, pourquoi des params ≠ de x ?
  this.add({ type: 'point', nom: '_J', par1: 0, par2: 1 })
  // puis ceux-là (à quoi ils servent ?)
  this.add({ nom: '$I', par1: this.pasdunegraduationX, par2: 0 })
  this.add({ nom: '$J', par1: 0, par2: this.pasdunegraduationY })
  this.add({ nom: '$L', v: 10 * this.pasdunegraduationY })
  this.add({ nom: '$K', par1: 10 * this.pasdunegraduationX, par2: 0 })
  // puis ceux fournis (qui ne pourront pas réutiliser les noms réservés des points déjà mis ci-dessus)
  for (const obj of props.objets) this.add(obj)
  // objetsByNom est désormais complet, et on veut interdire l’ajout direct d’objets (si la section affecte directement la propriété objets tant pis pour elle…)
  this.objets.push = (obj) => {
    console.error(Error('Il ne faut pas ajouter directement des objets au tableau d’objets, passez par la méthode add'))
    this.add(obj)
  }
} // Repere

Repere.prototype._check = function _check (props, type) {
  if (!props.nom) props.nom = this.getNom('d')
  if (props.type !== type) props.type = 'droite'
  if (!this.objetsByNom.has(props.nom)) this.add(props)
}

/**
 * Construit (ou reconstruit) tout le svg (le repère et ses objets)
 */
Repere.prototype.construit = function () {
  // le div contenant le svg
  if (this.divRepere) {
    if (this.divRepere.parentNode !== this.conteneur) {
      // bizarre, il est sorti du dom ? Parti ailleurs ? On s'étonne pas plus que ça et on le remet à sa place
      this.conteneur.appendChild(this.divRepere)
    }
    j3pEmpty(this.divRepere)
  } else {
    // ALEX : le positionnement absolu posait pb sous chrome avec la bibli...
    /** @type {HTMLElement} */
    this.divRepere = j3pAddElt(this.conteneur, 'div', '', { id: this.idDivRepere, style: { position: 'relative' } })
  }
  // création du svg, dans un span qui ne sert que pour le mouseout
  const svg = j3pCreeSVG(this.divRepere, { id: this.idRepere, width: this.larg, height: this.haut })
  this.svg = svg

  // les lignes qui suivent semblent totalement inutiles, pourquoi récupérer la largeur du svg qu’on vient de construire en lui imposant une largeur ?
  // la remarque de la ligne suivante semble complètement obsolète (width, height ne correspondent pas à un décalage, et _moveListener ne s’en sert pas)
  // et on note le décalage du svg (ça sert au mouseMove, pas la peine de le recalculer à chaque fois)
  const { width, height } = this.svg.getBoundingClientRect()
  // la taille
  // Dans les fênetres contruites par j3pCreeFenetres (comme à la correction de limites_ln1), this.larg et this.haut possèdent bien des valeurs
  // utilisées ligne 696. Sauf quand ligne 699, width et height valent tous deux 0. Je ne sais pas pourquoi
  // Voilà pourquoi j’ai ajouté ces if (width) et if (height) dans les lignes ci-dessous
  // @todo il faudra peut-être trouver d’où vient le pb et virer cette rustine
  if (width) this.larg = width
  if (height) this.haut = height
  // fin des lignes à virer

  // on veut mettre le listener au mousedown et le virer au mouseup, il faut donc une ref dessus
  // mais il faut aussi que la méthode construitpoint puisse le mettre (et que ce soit ce mouseup qui le vire)
  // => on l’ajoute en propriété
  this._moveListener = onMove.bind(this)
  // mouseup sur le document, pour toujours le choper (sauf si la souris sort de la fenêtre bouton enfoncé, d’ou le mouseout)
  const abort = () => {
    svg.removeEventListener('mousemove', this._moveListener, false)
    this.pointselectionne = ''
    this.hasMouseDown = false
  }
  svg.addEventListener('mouseup', abort)
  svg.addEventListener('mousedown', (event) => {
    if (this.fixe || event.target !== event.currentTarget) return
    // le svg déplaçable est le seul cas où on veut le moveListener même sans point sélectionné (pour translater le svg)
    this.hasMouseDown = true
    this._lastClickPos = getPosRelSouris(this, event)
    svg.addEventListener('mousemove', this._moveListener, false)
  })
  svg.addEventListener('mouseleave', (event) => {
    // il faut utiliser mouseleave est surtout pas mouseout (cf https://developer.mozilla.org/fr/docs/Web/API/Element/mouseleave_event)
    if (event.target !== event.currentTarget) return
    abort()
  }, false)
  // si on reconstruit le svg en étant appelé par onMove, il faut remettre le listener
  if (this.hasMouseDown) svg.addEventListener('mousemove', this._moveListener, false)

  if (this.visible || this.trame) {
    const epaisseur = this.visible ? 2 : 1
    const opacite = this.visible ? 1 : 0.3
    // les axes
    j3pCreeSegment(this.svg, {
      id: this._getId('axeX'),
      x1: 0,
      y1: this.yO,
      x2: this.larg,
      y2: this.yO,
      couleur: '#000000',
      epaisseur,
      opacite
    })

    j3pCreeSegment(this.svg, {
      id: this._getId('axeY'),
      x1: this.xO,
      y1: 0,
      x2: this.xO,
      y2: this.haut,
      couleur: '#000000',
      epaisseur,
      opacite
    })

    // Rémi - Modif de l’option fixe dans chacune des 4 lignes (this.fixe et non plus false)'
    if (this.visible) {
      // Si c’est pas fixe faut créer un point pour pouvoir le bouger
      // Si fixe, son étiquette suffirait, mais on crée quand même le point pour rester homogène
      // (O est un point réservé et ne peut être ajouté)
      this.construitpoint({
        nom: 'O',
        visible: false,
        abscisse: 0,
        ordonnee: 0,
        taillepoint: 3,
        taillepolice: 16,
        police: 'sans-serif',
        couleur: this.couleur.ptm,
        epaisseur: 2,
        nbdecimales: 2,
        fixe: this.fixe
      })
      // étiquette de l’origine
      j3pCreeTexte(this.svg, {
        x: this.xO + 3,
        y: this.yO + 15,
        texte: '0',
        taille: 12,
        couleur: 'blue',
        italique: false,
        fonte: 'serif'
      })
    }

    let k
    // graduations de (Ox)
    const nbLignesVert = Math.floor((this.larg - this.xO) / this.pixelspargraduationX)
    for (k = 1; k <= nbLignesVert; k++) {
      const x = this.xO + k * this.pixelspargraduationX
      if (this.visible) {
        j3pCreeSegment(this.svg, {
          x1: x,
          y1: this.yO - 4,
          x2: x,
          y2: this.yO + 4,
          couleur: '#000000',
          epaisseur: 2,
          opacite: 1
        })
        j3pCreeTexte(this.svg, {
          x: x - 5,
          y: this.yO + 15,
          texte: this.pasdunegraduationX * k,
          taille: 12,
          couleur: 'blue',
          fonte: 'serif',
          bold: true
        })
      }
      if (this.trame) {
        // ligne verticale de quadrillage
        j3pCreeSegment(this.svg, {
          x1: x,
          y1: this.negatifs ? 1500 : this.yO,
          x2: x,
          y2: 0,
          couleur: '#000000',
          epaisseur: 1,
          opacite: 0.3
        })
      }
    }

    // graduations de (Oy)
    for (k = 1; k <= Math.floor((this.yO) / this.pixelspargraduationY); k++) {
      const y = this.yO - k * this.pixelspargraduationY
      if (this.visible) {
        j3pCreeSegment(this.svg, {
          x1: this.xO - 4,
          y1: y,
          x2: this.xO + 4,
          y2: y,
          couleur: '#000000',
          epaisseur: 2,
          opacite: 1
        })
        j3pCreeTexte(this.svg, {
          x: -2 + this.xO - decalage('' + j3pArrondi(Number(this.debuty) + this.pasdunegraduationY * k, 2)),
          y: y + 5,
          texte: j3pArrondi(Number(this.debuty) + this.pasdunegraduationY * k, 2),
          taille: 12,
          couleur: 'blue',
          italique: false,
          fonte: 'serif',
          bold: true
        })
      }
      if (this.trame) {
        // ligne horizontale de quadrillage
        if (this.negatifs) {
          j3pCreeSegment(this.svg, {
            x1: 0,
            y1: y,
            x2: 1500,
            y2: y,
            couleur: '#000000',
            epaisseur: 1,
            opacite: 0.3
          })
        } else {
          j3pCreeSegment(this.svg, {
            x1: this.xO,
            y1: y,
            x2: 1500,
            y2: y,
            couleur: '#000000',
            epaisseur: 1,
            opacite: 0.3
          })
        }
      }
    }

    // idem pour les négatifs
    if (this.negatifs) {
      // x négatifs
      for (k = 1; k <= Math.floor((this.xO) / this.pixelspargraduationX); k++) {
        const x = this.xO - k * this.pixelspargraduationX
        if (this.visible) {
          j3pCreeSegment(this.svg, {
            x1: x,
            y1: this.yO - 4,
            x2: x,
            y2: this.yO + 4,
            couleur: '#000000',
            epaisseur: 2,
            opacite: 1
          })
          j3pCreeTexte(this.svg, {
            x: x - 5,
            y: this.yO + 15,
            texte: '-' + this.pasdunegraduationX * k,
            taille: 12,
            couleur: 'blue',
            italique: false,
            fonte: 'serif',
            bold: true
          })
        }
        if (this.trame) {
          j3pCreeSegment(this.svg, {
            x1: x,
            y1: 1500,
            x2: x,
            y2: 0,
            couleur: '#000000',
            epaisseur: 1,
            opacite: 0.3
          })
        }
      }

      // y négatifs
      for (k = 1; k <= Math.floor((this.haut - this.yO) / this.pixelspargraduationY); k++) {
        const y = this.yO + k * this.pixelspargraduationY
        if (this.visible) {
          j3pCreeSegment(this.svg, {
            x1: this.xO - 4,
            y1: y,
            x2: this.xO + 4,
            y2: y,
            couleur: '#000000',
            epaisseur: 2,
            opacite: 1
          })
          j3pCreeTexte(this.svg, {
            x: -2 + this.xO - decalage('-' + this.pasdunegraduationY * k),
            y: y + 5,
            texte: '-' + this.pasdunegraduationY * k,
            taille: 12,
            couleur: 'blue',
            italique: false,
            fonte: 'serif',
            bold: true
          })
        }
        if (this.trame) {
          j3pCreeSegment(this.svg, {
            x1: 0,
            y1: y,
            x2: 1500,
            y2: y,
            couleur: '#000000',
            epaisseur: 1,
            opacite: 0.3
          })
        }
      }
    }

    if (this.visible && !this.fixe) {
      // on ajoute les points pour changer l’échelle (revient à zoom/dézoom)
      this.construitpoint({
        nom: '$I',
        visible: false,
        abscisse: this.pasdunegraduationX,
        ordonnee: 0,
        taillepoint: 3,
        taillepolice: 16,
        police: 'sans-serif',
        couleur: this.couleur.ptm,
        epaisseur: 2,
        nbdecimales: 2,
        fixe: this.fixe
      })
      // this.add({nom:'I',x:this.pasdunegraduationX,y:0})
      this.construitpoint({
        nom: '$J',
        visible: false,
        abscisse: 0,
        ordonnee: this.pasdunegraduationY,
        taillepoint: 3,
        taillepolice: 16,
        police: 'sans-serif',
        couleur: this.couleur.ptm,
        epaisseur: 2,
        nbdecimales: 2,
        fixe: this.fixe
      })
      // $L à (0, 10), pour quoi faire ?
      this.construitpoint({
        nom: '$L',
        visible: false,
        abscisse: 0,
        ordonnee: 10 * this.pasdunegraduationY,
        taillepoint: 3,
        taillepolice: 16,
        police: 'sans-serif',
        couleur: this.couleur.ptm,
        epaisseur: 2,
        nbdecimales: 2,
        fixe: this.fixe
      })
      // $K à (10, 0)
      this.construitpoint({
        nom: '$K',
        visible: false,
        abscisse: 10 * this.pasdunegraduationX,
        ordonnee: 0,
        taillepoint: 3,
        taillepolice: 16,
        police: 'sans-serif',
        couleur: this.couleur.ptm,
        epaisseur: 2,
        nbdecimales: 2,
        fixe: this.fixe
      })
    }
  } // fin du cas visible ou trame

  // on ajoute les objets fournis au constructeur ou ajoutés ensuite
  construitObjets(this)
}

Repere.prototype.RenvoiePixels = function (abscisse, ordonnee) {
  // 50 pixels signifie 50pixels à droite du centre de la droite graduée, cad du SVG la contenant.
  return [
    this.xO + (abscisse / this.pasdunegraduationX) * this.pixelspargraduationX,
    this.yO - ((ordonnee) / this.pasdunegraduationY) * this.pixelspargraduationY
  ]
}

/**
 * Attention, objet n’a pas les propriétés d’un élément de this.objets !!!
 * Cf Repere.prototype.point
 * @param objet
 */
Repere.prototype.construitpoint = function (objet) {
  // objet={nom,abscisse,ordonnee,etiquette,taillepoint,taillepolice,police,couleur,epaisseur,nbdecimales,fixe}
  const repere = this
  const coordM = this.RenvoiePixels(objet.abscisse, objet.ordonnee)

  if (typeof objet.taillepoint === 'undefined') objet.taillepoint = 0
  if (typeof objet.visible === 'undefined') objet.visible = true
  const nomToId = this._idCleaner(objet.nom)
  if (objet.visible && (objet.taillepoint || this._weakMode)) {
    // en _weakMode faut continuer à créer des segments de longueur nulle, sinon la section cherche ces ids et plante
    j3pCreeSegment(this.svg, {
      id: this._getId('ligne1' + nomToId),
      className: 'croixPoint',
      x1: coordM[0] - objet.taillepoint,
      y1: coordM[1] - objet.taillepoint,
      x2: coordM[0] + objet.taillepoint,
      y2: coordM[1] + objet.taillepoint,
      couleur: objet.couleur,
      epaisseur: objet.epaisseur
    })
    j3pCreeSegment(this.svg, {
      id: this._getId('ligne2' + nomToId),
      className: 'croixPoint',
      x1: coordM[0] + objet.taillepoint,
      y1: coordM[1] - objet.taillepoint,
      x2: coordM[0] - objet.taillepoint,
      y2: coordM[1] + objet.taillepoint,
      couleur: objet.couleur,
      epaisseur: objet.epaisseur
    })
  }

  if (objet.etiquette) {
    let dx, dy
    if (typeof objet.decal === 'undefined') {
      dx = -9
      dy = -14
    } else if (typeof objet.decal === 'string') {
      let posVirgule = objet.decal.indexOf(',')
      let objNom = objet.decal.substring(0, posVirgule)
      const obj1 = this.getObjet(objNom)
      objet.decal = objet.decal.substring(posVirgule + 1)
      posVirgule = objet.decal.indexOf(',')
      objNom = objet.decal.substring(0, posVirgule)
      const obj2 = this.getObjet(objNom)
      objNom = objet.decal.substring(1 + posVirgule)
      const obj3 = this.getObjet(objNom)
      const pB = [obj1.x, obj1.y]
      const pA = [obj2.x, obj2.y]
      const pC = [obj3.x, obj3.y]
      const d1 = j3pDistance(pA, pB)
      const d2 = j3pDistance(pA, pC)
      const vecAB = [(pB[0] - pA[0]) / d1, (pB[1] - pA[1]) / d1]
      const vecAC = [(pC[0] - pA[0]) / d2, (pC[1] - pA[1]) / d2]
      let vecu = [-vecAB[0] - vecAC[0], -vecAB[1] - vecAC[1]]
      const d3 = j3pDistance([0, 0], vecu)
      vecu = [vecu[0] / d3, vecu[1] / d3]
      const poseti = [pA[0] + vecu[0] / 1.5, pA[1] + vecu[1] / 1.5]
      dx = this.RenvoiePixels(poseti[0], poseti[1])[0] - this.RenvoiePixels(pA[0], pA[1])[0]
      dy = this.RenvoiePixels(poseti[1], poseti[1])[1] - this.RenvoiePixels(pA[0], pA[1])[1]
      if ((pA[1] > pB[1]) && (pA[1] > pC[1])) {
        if ((pA[0] < pB[0]) && (pA[0] < pC[0])) {
          // au-dessus à gauche des 2
          dx += 0
          dy += 0
        } else {
          dx -= 10
          dy += 5
        }
      } else {
        if ((pA[0] < pB[0]) && (pA[0] < pC[0])) {
          // pas au-dessus mais à gauche
          dx += 0
          dy += 0
        } else {
          dx += 5
          dy += 0
        }
      }
    } else {
      dx = -9 + objet.decal[0]
      dy = -14 + objet.decal[1]
    }

    if (objet.visible) {
      j3pCreeTexte(this.svg, {
        id: this._getId('nompoint' + nomToId),
        x: coordM[0] + dx,
        y: coordM[1] + dy,
        texte: objet.label || objet.nom,
        taille: objet.taillepolice,
        couleur: objet.couleur,
        italique: objet.italique,
        fonte: objet.police
      })
    }
  }

  if (!objet.fixe) {
    // on crée un cadre autour du point (12px de coté) pour pouvoir le capturer
    const lecadrepoint = j3pCreeRectangle(this.svg, {
      id: this._getId('cadrepoint' + nomToId),
      x: coordM[0] - 6,
      y: coordM[1] - 6,
      width: 12,
      height: 12,
      couleur: 'black',
      couleurRemplissage: '#F00',
      opaciteRemplissage: 0.01,
      epaisseur: 0
    })
    lecadrepoint.addEventListener('mousedown', (event) => {
      if (event.target !== lecadrepoint) return
      repere.pointselectionne = objet.nom
      repere.hasMouseDown = true
      repere.svg.addEventListener('mousemove', repere._moveListener, false)
    })
    // pas besoin de mouseup (qui faisait la même chose que le listener sur le svg, et en plus virait lecadrepoint
    // ce qui était un bug ! (si on clique sur le point sans le bouger assez pour recontruire le tout,
    // puis qu’on le lâche => on ne pouvait plus le recliquer)
  }
} // construitpoint

/**
 * Retourne la propriété x de l’objet
 * @param {string} nomObjet
 * @return {number}
 */
Repere.prototype.abs = function (nomObjet) {
  return this.getObjet(nomObjet).x
}

/**
 * Retourne un nom qui n’existe pas encore dans le repère, soit prefix soit prefix_nn où nn est un entier (démarre à 2)
 * @param prefix
 * @return {string}
 */
Repere.prototype.getNom = function getNom (prefix) {
  if (!prefix || typeof prefix !== 'string') throw Error(`nom ${prefix} invalide`)
  let i = 2
  let nom = prefix
  while (this.objetsByNom.has(nom)) nom = `${prefix}_${i++}`
  return nom
}

/**
 * Ajoute un objet au repère (sans le construire)
 * Accepte plusieurs params.
 * @param {Object} objet
 */
Repere.prototype.add = function add (objet) {
  // au cas où il resterait des appels avec plusieurs arguments
  if (arguments.length > 1) {
    j3pNotify(Error('appel de repere.add avec plusieurs arguments'))
    for (const arg of arguments) this.add(arg)
    return
  }
  if (!objet || typeof objet !== 'object') throw Error('objet invalide')
  if (objet.nom && typeof objet.nom !== 'string') throw Error('nom invalide')
  if (!objet.nom) objet.nom = this.getNom('_obj')
  const { nom } = objet
  if (this.objetsByNom.has(nom)) {
    objet.nom = this.getNom(nom)
    console.error(Error(`Il y a déjà un objet avec le nom ${nom}, ce nom sera remplacé par ${objet.nom}`))
  }
  this.objetsByNom.set(objet.nom, objet)
  // on peut plus utiliser this.objets.push() puisqu’on la redéfinit et ça nous appelle
  Array.prototype.push.call(this.objets, objet)
}

/**
 * Retire l’objet nom
 * @param {string} nom
 * @return {boolean} false s’il n’y était pas, true sinon
 */
Repere.prototype.remove = function remove (nom) {
  if (!this.objetsByNom.has(nom)) return false
  this.objetsByNom.remove(nom)
  // on pourrait utiliser Array.from(this.objetsByNom) mais l’ordre ne serait pas forcément conservé
  this.objets = this.objets.filter(o => o.nom !== nom)
}

/**
 * Retourne la propriété y de l’objet
 * @param {string} nomObjet
 * @return {number}
 */
Repere.prototype.ord = function (nomObjet) {
  return this.getObjet(nomObjet).y
}

/**
 * Init l’objet point et appelle construitpoint
 * @param {Object} props les propriétés de l’objet point
 * @param {number} props.x
 * @param {number} props.y
 * @param {boolean} props.fixe
 * @param {boolean} props.etiquette s’il faut afficher le libellé du point (label ou nom)
 * @param {Object} props.style
 * @param {string} props.label
 * @param style
 */
Repere.prototype.point = function point (objPoint) {
  this._check(objPoint, 'point')
  const { nom, x, y, fixe, etiquette, style = {}, label } = objPoint
  this.construitpoint({
    nom,
    decal: style.decal || [0, 0],
    abscisse: x,
    ordonnee: y,
    taillepoint: style.taillepoint,
    taillepolice: style.taille,
    police: 'sans-serif',
    italique: style.italique,
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    nbdecimales: 2,
    fixe,
    etiquette,
    label
  })
}

/**
 * Ajoute l’objet de type pointsur au svg
 * @param {RepereObject} props
 * @param {} props.nom
 * @param {} props.par1
 * @param {} props.par2
 * @param {} props.fixe
 * @param {} props.etiquette
 * @param {} props.style
 * @param {} props.label
 */
Repere.prototype.pointsur = function pointsur (objPoint) {
  this._check(objPoint, 'pointsur')
  const { nom, par1, par2, fixe, etiquette, style, label } = objPoint
  let ext1, ext2
  if (typeof par2 === 'string') {
    // on suppose l’objet de ref être droite|segment|demidroite, mais on vérifie rien…
    ext1 = par1
    ext2 = par2
  } else {
    // on ignore par2 et on suppose par1 suffisant (un segment ?)
    const objRef = this.getObjet(par1)
    if (objRef.type === 'cercle') {
      const rapport = this.pixelspargraduationX / this.pasdunegraduationX
      const rayon = objRef.rayon / rapport
      const objCentre = this.getObjet(objRef.par1)
      const coord = [objCentre.x, objCentre.y]
      objPoint.x = coord[0] + Math.cos((2 * par2 / 360) * Math.PI) * rayon
      objPoint.y = coord[1] + Math.sin((2 * par2 / 360) * Math.PI) * rayon
    } else {
      ext1 = objRef.par1
      ext2 = objRef.par2
    }
  }

  const dx = style.decal?.[0] ?? 0
  const dy = style.decal?.[1] ?? 0
  let pt1, pt2
  // attention, ext1 et ext2 ne sont pas initialisé pour le cercle en par1 (sans par2)
  if (ext1) {
    const { x, y } = this.getObjet(ext1)
    pt1 = [x, y]
    const objPt2 = this.getObjet(ext2)
    pt2 = [objPt2.x, objPt2.y]
    objPoint.x = pt1[0] + par2 * (pt2[0] - pt1[0])
    objPoint.y = pt1[1] + par2 * (pt2[1] - pt1[1])
  }
  this.construitpoint({
    visible: objPoint.visible,
    nom,
    decal: [dx, dy],
    abscisse: objPoint.x,
    ordonnee: objPoint.y,
    taillepoint: style.taillepoint,
    taillepolice: style.taille,
    police: 'sans-serif',
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    nbdecimales: 2,
    fixe,
    etiquette,
    label
  })

  if (style.codage && pt1 && pt1) {
    // seulement pour les milieux
    const rapport = this.pixelspargraduationX / this.pasdunegraduationX
    const vecteurU = normalise([pt2[0] - pt1[0], pt2[1] - pt1[1]])
    const vecteurV = j3pImageRotation(0, 0, 45, vecteurU[0], vecteurU[1])

    const milieu1 = [(pt1[0] + objPoint.x) / 2, (pt1[1] + objPoint.y) / 2]
    const taille = 3
    const taille2 = 6
    const milieu1avant = [milieu1[0] - (taille / rapport) * vecteurU[0], milieu1[1] - (taille / rapport) * vecteurU[1]]

    let ext1milieu1avant, ext2milieu1avant
    if (style.codage === '/') {
      ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
      ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu1avant[0],
        y1: ext1milieu1avant[1],
        x2: ext2milieu1avant[0],
        y2: ext2milieu1avant[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
      ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu1avant[0],
        y1: ext1milieu1avant[1],
        x2: ext2milieu1avant[0],
        y2: ext2milieu1avant[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })

      const milieu1apres = [milieu1[0] + (taille / rapport) * vecteurU[0], milieu1[1] + (taille / rapport) * vecteurU[1]]
      const ext1milieu1apres = this.RenvoiePixels(milieu1apres[0] - (taille2 / rapport) * vecteurV[0], milieu1apres[1] - (taille2 / rapport) * vecteurV[1])
      const ext2milieu1apres = this.RenvoiePixels(milieu1apres[0] + (taille2 / rapport) * vecteurV[0], milieu1apres[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu1apres[0],
        y1: ext1milieu1apres[1],
        x2: ext2milieu1apres[0],
        y2: ext2milieu1apres[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    }

    const milieu2 = [(pt2[0] + objPoint.x) / 2, (pt2[1] + objPoint.y) / 2]

    let ext1milieu2avant, ext2milieu2avant
    const milieu2avant = [milieu2[0] - (taille / rapport) * vecteurU[0], milieu2[1] - (taille / rapport) * vecteurU[1]]
    if (style.codage === '/') {
      ext1milieu2avant = this.RenvoiePixels(milieu2[0] - (taille2 / rapport) * vecteurV[0], milieu2[1] - (taille2 / rapport) * vecteurV[1])
      ext2milieu2avant = this.RenvoiePixels(milieu2[0] + (taille2 / rapport) * vecteurV[0], milieu2[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu2avant[0],
        y1: ext1milieu2avant[1],
        x2: ext2milieu2avant[0],
        y2: ext2milieu2avant[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      ext1milieu2avant = this.RenvoiePixels(milieu2avant[0] - (taille2 / rapport) * vecteurV[0], milieu2avant[1] - (taille2 / rapport) * vecteurV[1])
      ext2milieu2avant = this.RenvoiePixels(milieu2avant[0] + (taille2 / rapport) * vecteurV[0], milieu2avant[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu2avant[0],
        y1: ext1milieu2avant[1],
        x2: ext2milieu2avant[0],
        y2: ext2milieu2avant[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })

      const milieu2apres = [milieu2[0] + (taille / rapport) * vecteurU[0], milieu2[1] + (taille / rapport) * vecteurU[1]]
      const ext1milieu2apres = this.RenvoiePixels(milieu2apres[0] - (taille2 / rapport) * vecteurV[0], milieu2apres[1] - (taille2 / rapport) * vecteurV[1])
      const ext2milieu2apres = this.RenvoiePixels(milieu2apres[0] + (taille2 / rapport) * vecteurV[0], milieu2apres[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu2apres[0],
        y1: ext1milieu2apres[1],
        x2: ext2milieu2apres[0],
        y2: ext2milieu2apres[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    }
  } else if (style.codage) {
    console.error(Error('On ne peut pas utiliser le codage sur un cercle (option codage ignorée)'))
  }
}

/**
 * Retourne l’équation cartésienne d’une droite
 * @param d
 * @return {number[]} Un tableau de 3 nombres (vide si c’est un truc non géré)
 */
Repere.prototype.equation = function (d) {
  // retourne l’équation cartésienne
  const objet = this.getObjet(d)
  const { cd, type } = objet
  switch (type) {
    case 'droite':
    case 'demidroite': {
      const pt1 = this.getObjet(objet.par1)
      const pt2 = this.getObjet(objet.par2)
      const xp1 = pt1.par1
      const yp1 = pt1.par2
      const xp2 = pt2.par1
      const yp2 = pt2.par2
      return [-(yp2 - yp1), xp2 - xp1, -(yp2 - yp1) * xp1 + (xp2 - xp1) * yp1]
    }

    case 'droiteparallele': {
      const { par1, par2 } = this.getObjet(objet.par1)
      if (cd === Number.POSITIVE_INFINITY) return [-1, 0, par1]
      return [-cd, 1, -cd * par1 + par2]
    }

    case 'droiteperpendiculaire': {
      const { x, y } = this.getObjet(objet.par1)
      if (cd === Number.POSITIVE_INFINITY) return [1, 0, x]
      if (cd === 0) return Number.POSITIVE_INFINITY
      return [-cd, 1, -cd * x + y]
    }
    default:
      console.error(Error('Le type d’objet ' + type + ' n’est pas valide pour trouver l’équation cartésienne d’une droite'))
      return []
  }
}

/**
 * Retourne la distance entre deux points
 * @param {string} nom1
 * @param {string} nom2
 * @return {number}
 */
Repere.prototype.distance = function (nom1, nom2) {
  const p1 = this.getObjet(nom1)
  const p2 = this.getObjet(nom2)
  return j3pDistance(p1.x, p1.y, p2.x, p2.y)
}

Repere.prototype.intersection = function (d1, d2) {
  const tab1 = this.equation(d1)
  const tab2 = this.equation(d2)
  const det = tab1[0] * tab2[1] - tab1[1] * tab2[0]
  if (!det) return null
  return [
    (tab1[2] * tab2[1] - tab1[1] * tab2[2]) / det,
    (tab1[0] * tab2[2] - tab2[0] * tab1[2]) / det
  ]
}

// tab = [a,b,c] : ax+by+c=0
Repere.prototype.intersection2 = function (d1, tab) {
  const tab1 = this.equation(d1)
  const tab2 = tab
  const det = tab1[0] * tab2[1] - tab1[1] * tab2[0]
  if (det === 0) return null
  return [(tab1[2] * tab2[1] - tab1[1] * tab2[2]) / det, (tab1[0] * tab2[2] - tab2[0] * tab1[2]) / det]
}

Repere.prototype.projeteOrthogonal = function (tab, coord) {
  const xM = coord[0]
  const yM = coord[1]
  const xH = (tab[1] * tab[1] * xM - tab[0] * tab[1] * yM - tab[0] * tab[2]) / (tab[0] * tab[0] + tab[1] * tab[1])
  const yH = (tab[0] * tab[0] * yM - tab[0] * tab[1] * xM - tab[1] * tab[2]) / (tab[0] * tab[0] + tab[1] * tab[1])

  return [xH, yH]
}

Repere.prototype.pointintersection = function (pt1, par1, par2, pfixe, petiquette, style) {
  const point = this.getObjet(pt1)
  const inter = this.intersection(par1, par2)
  if (inter !== 'vide') {
    const x1 = inter[0]
    const y1 = inter[1]
    point.x = x1
    point.y = y1
    this.construitpoint({
      nom: pt1,
      abscisse: x1,
      ordonnee: y1,
      taillepoint: style.taillepoint,
      taillepolice: style.taille,
      police: 'sans-serif',
      decal: style.decal,
      italique: style.italique,
      couleur: style.couleur,
      epaisseur: style.epaisseur,
      nbdecimales: 2,
      fixe: pfixe,
      etiquette: petiquette,
      label: point.label
    })
  }
}

/**
 * ex d’appel this.construitsegment('L','K',{couleur:'#000',epaisseur:2})
 * @param {Object} props
 * @param {string} [props.nom]
 * @param {string} props.par1
 * @param {string} props.par2
 * @param {Object} props.style style du segment
 * @param {Object} [props.graduations] Le style des graduations (si omis y’en a pas)
 */
Repere.prototype.segment = function (props) {
  if (!props.nom) props.nom = this.getNom('_')
  const { par1: pt1, par2: pt2, style, graduations } = props
  if (!pt1 || !pt2) {
    console.error(Error('props invalides pour un segment'), props)
  }
  const ptext1 = this.getObjet(pt1)
  if (!ptext1) return // getObjet a déjà ralé
  let tab = this.RenvoiePixels(ptext1.x, ptext1.y)
  let xpt1 = tab[0]
  let ypt1 = tab[1]
  const ptext2 = this.getObjet(pt2)
  if (!ptext2) return
  tab = this.RenvoiePixels(ptext2.x, ptext2.y)
  let xpt2 = tab[0]
  let ypt2 = tab[1]
  if (isNaN(xpt1)) { xpt1 = ptext1.par1 }
  if (isNaN(ypt1)) { ypt1 = ptext1.par2 }
  if (isNaN(xpt2)) { xpt2 = ptext2.par1 }
  if (isNaN(ypt2)) { ypt2 = ptext2.par2 }
  j3pCreeSegment(this.svg, {
    id: this._getId('segment' + this._idCleaner(pt1 + pt2)),
    x1: xpt1,
    y1: ypt1,
    x2: xpt2,
    y2: ypt2,
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    pointilles: style.pointilles
  })

  if (typeof graduations !== 'undefined') {
    j3pGradueSegment(this.svg, {
      x1: xpt1,
      y1: ypt1,
      x2: xpt2,
      y2: ypt2,
      style: graduations
    })
  }

  if (style.codage) {
    // seulement pour les milieux
    const rapport = this.pixelspargraduationX / this.pasdunegraduationX
    const vecteurU = normalise([pt2[0] - pt1[0], pt2[1] - pt1[1]])
    const vecteurV = j3pImageRotation(0, 0, 45, vecteurU[0], vecteurU[1])

    const milieu1 = [(pt1[0] + pt2[0]) / 2, (pt1[1] + pt2[1]) / 2]
    const taille = 3
    const taille2 = 6
    const milieu1avant = milieu1
    let ext1milieu1avant, ext2milieu1avant

    if (style.codage === '/') {
      ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
      ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu1avant[0],
        y1: ext1milieu1avant[1],
        x2: ext2milieu1avant[0],
        y2: ext2milieu1avant[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
      ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu1avant[0],
        y1: ext1milieu1avant[1],
        x2: ext2milieu1avant[0],
        y2: ext2milieu1avant[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })

      const milieu1apres = [milieu1[0] + (taille / rapport) * vecteurU[0], milieu1[1] + (taille / rapport) * vecteurU[1]]
      const ext1milieu1apres = this.RenvoiePixels(milieu1apres[0] - (taille2 / rapport) * vecteurV[0], milieu1apres[1] - (taille2 / rapport) * vecteurV[1])
      const ext2milieu1apres = this.RenvoiePixels(milieu1apres[0] + (taille2 / rapport) * vecteurV[0], milieu1apres[1] + (taille2 / rapport) * vecteurV[1])
      j3pCreeSegment(this.svg, {
        x1: ext1milieu1apres[0],
        y1: ext1milieu1apres[1],
        x2: ext2milieu1apres[0],
        y2: ext2milieu1apres[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    }
  }
}

/**
 * @param nom
 * @param par1
 * @param par2
 * @param par3
 * @param par4
 * @param style
 */
Repere.prototype.secteur = function (nom, par1, par2, par3, par4, style) {
  // console.warn(Error('Il faut remplacer cette méthode par l’usage de j3pCreeSecteur'))
  const pt1 = this.getObjet(par1)
  const coord1 = [pt1.x, pt1.y]
  const tab1 = this.RenvoiePixels(coord1[0], coord1[1])
  const lerayon = Number(par4) * this.pixelspargraduationX
  j3pCreeSecteur(this.svg, {
    id: this._getId('secteur' + this._idCleaner(nom)),
    centreX: tab1[0],
    centreY: tab1[1],
    rayon: lerayon,
    angleDebut: Number(par2),
    anglefin: Number(par3),
    couleur: style.couleur,
    opacite: style.opacite
  })
}

/**
 * @param nom
 * @param par1
 * @param par2
 * @param par3
 * @param par4
 * @param style
 */
Repere.prototype.angle = function (nom, par1, par2, par3, par4, style) {
  const objPar1 = this.getObjet(par1)
  const coord1 = [objPar1.x, objPar1.y]
  const objPar2 = this.getObjet(par2)
  const coord2 = [objPar2.x, objPar2.y]
  const objPar3 = this.getObjet(par3)
  const coord3 = [objPar3.x, objPar3.y]
  const vec21 = [objPar1.x - objPar2.x, objPar1.y - objPar2.y]
  const norm21 = j3pDistance(coord1, coord2)
  const pt1 = [objPar2.x + (par4 / norm21) * vec21[0], objPar2.y + (par4 / norm21) * vec21[1]]

  const vec23 = [coord3[0] - coord2[0], coord3[1] - coord2[1]]
  const norm23 = j3pDistance(coord3, coord2)
  const pt3 = [coord2[0] + (par4 / norm23) * vec23[0], coord2[1] + (par4 / norm23) * vec23[1]]

  const tab1 = this.RenvoiePixels(pt1[0], pt1[1])
  const tab2 = this.RenvoiePixels(coord2[0], coord2[1])
  const tab3 = this.RenvoiePixels(pt3[0], pt3[1])

  j3pCreeAngle(this.svg, {
    id: this._getId('angle' + this._idCleaner(par1 + par2 + par3)),
    tab1,
    tab2,
    tab3,
    epaisseur: style.epaisseur,
    couleur: style.couleur,
    opacite: style.opacite
  })
}

/**
 * @param nom
 * @param par1
 * @param par2
 * @param style
 * @return {SVGCircleElement}
 */
Repere.prototype.cercle = function (nom, par1, par2, style) {
  const pt1 = this.getObjet(par1)
  const [x1, y1] = this.RenvoiePixels(pt1.x, pt1.y)
  const props = {
    id: this._getId('svgcercle' + this._idCleaner(par1 + par2)),
    cx: x1 + 'px',
    cy: y1 + 'px',
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    couleurRemplissage: style.couleurRemplissage,
    opaciteRemplissage: style.opaciteRemplissage
  }
  const obj = this.getObjet(nom)
  if (typeof par2 === 'number') {
    // c’est le rayon
    obj.rayon = par2
    props.rayon = par2
  } else if (typeof par2 === 'string') {
    // c’est un point
    const objPar2 = this.getObjet(par2)
    const [x2, y2] = this.RenvoiePixels(objPar2.x, objPar2.y)
    const vec = [x2 - x1, y2 - y1]
    const rayon = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1])
    obj.rayon = rayon
    props.rayon = rayon
  } else {
    console.error(Error('par2 invalide pour construire le cercle', par2))
    return null
  }
  return j3pCreeCercle(this.svg, props)
}

/**
 *
 * @param {string} nom nom du cercle (qui doit déjà exister dans les objets…)
 * @param {string} par1 nom d’un point du diamètre
 * @param {string} par2 nom de l’autre point du diamètre
 * @param {Object} style
 * @return {SVGCircleElement}
 */
Repere.prototype.cercledia = function (nom, par1, par2, style) {
  const cercle = this.getObjet(nom)
  const p1 = this.getObjet(par1)
  const [x1, y1] = this.RenvoiePixels(p1.x, p1.y)
  const p2 = this.getObjet(par2)
  const [x2, y2] = this.RenvoiePixels(p2.x, p2.y)

  const vec = [(x2 - x1) / 2, (y2 - y1) / 2]
  const rayon = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1])
  cercle.rayon = rayon
  const xOrig = (x1 + x2) / 2
  const yOrig = (y1 + y2) / 2
  return j3pCreeCercle(this.svg, {
    id: this._getId('cercledia' + this._idCleaner(nom)),
    cx: xOrig + 'px',
    cy: yOrig + 'px',
    rayon,
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    couleurRemplissage: style.couleurRemplissage,
    opaciteRemplissage: style.opaciteRemplissage
  })
}

Repere.prototype.distancepointdroite = function (pt, d) {
  const { x, y } = this.getObjet(pt)
  const eq = this.equation(d)
  return (Math.abs(eq[0] * x + eq[1] * y - eq[2]) / Math.sqrt(eq[0] * eq[0] + eq[1] * eq[1]))
}

Repere.prototype.polygone = function (nom, points, style) {
  // points = ['A',...]
  const coords = points.map(point => {
    const objPoint = this.getObjet(point)
    let px, py
    if ((objPoint.type === 'pointsur') || (objPoint.type === 'pointintersection')) {
      px = objPoint.x
      py = objPoint.y
    } else {
      px = objPoint.par1
      py = objPoint.par2
    }
    return this.RenvoiePixels(px, py)
  })
  j3pPolygone(this.svg, {
    id: this._getId('id' + this._idCleaner(nom)),
    tab: coords,
    couleur: style.couleur,
    couleurRemplissage: style.couleurRemplissage,
    opaciteRemplissage: style.opaciteRemplissage,
    epaisseur: style.epaisseur
  })
}

Repere.prototype.face = function (nom, par1, style) {
  creeFace(this.svg, {
    id: this._getId('arc' + this._idCleaner(nom)),
    d: par1,
    couleur: style.couleur,
    couleurRemplissage: style.couleurRemplissage,
    opaciteRemplissage: style.opaciteRemplissage,
    pointilles: style.pointilles
  })
}

Repere.prototype.ellipse3 = function (nom, cx, cy, rx, ry, angledeb, anglefin, rotation, style) {
  // par1 Nomcentre  par2 rayon par3 angleDebut par4 anglefin
  const Rx = Number(rx) * this.pixelspargraduationX
  const Ry = Number(ry) * this.pixelspargraduationX
  const Angledeb = Number(angledeb)
  let Anglefin = Number(anglefin)
  if (Anglefin < Angledeb) { Anglefin += 360 }
  const Rotation = Number(rotation)
  const Cx = Number(cx) * this.pixelspargraduationX
  const Cy = 300 - (Number(cy) * this.pixelspargraduationX)
  creeEllipse3(this.svg, {
    id: this._getId('arc' + this._idCleaner(nom)),
    centreX: Cx,
    centreY: Cy,
    rx: Rx,
    ry: Ry,
    angledeb: Angledeb,
    anglefin: Anglefin,
    rotation: Rotation,
    style,
    grad: this.pixelspargraduationX
  })
}

Repere.prototype.ellipse2 = function (nom, par1, par2, par3, par4, par5, par6, par7, par8, par9, style) {
  // par1 Nomcentre  par2 rayon par3 angledebut par4 anglefin
  const rx = Number(par5) * this.pixelspargraduationX
  const ry = Number(par6) * this.pixelspargraduationX
  const cx = Number(par1) * this.pixelspargraduationX
  const cy = Number(par2) * this.pixelspargraduationX
  const ax = Number(par3) * this.pixelspargraduationX
  const ay = Number(par4) * this.pixelspargraduationX
  const c7 = Number(par7) * this.pixelspargraduationX
  const c8 = Number(par8) * this.pixelspargraduationX
  const c9 = Number(par9) * this.pixelspargraduationX

  creeEllipse2(this.svg, {
    id: this._getId('arc' + this._idCleaner(nom)),
    centreX: cx,
    centreY: cy,
    rx,
    ry,
    ArriveX: ax,
    ArriveY: ay,
    p7: c7,
    p8: c8,
    p9: c9,
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    couleurIn: style.couleur_remplissage,
    opacite: style.opacite_remplissage,
    pointilles: style.pointilles
  })
}

// this.prisme(this.objets[k].nom,this.objets[k].par1,this.objets[k].par2,this.objets[k].par3,this.objets[k].par4,this.objets[k].style)
Repere.prototype.prisme = function (nom, par1, par2, par3, par4, par5, style) {
  // par3 peut être du type 'distance(p1,p2)'
  if (typeof par3 === 'string') {
    // donc distance

    const p1 = par3.substring(9, par3.indexOf(','))
    const p2 = par3.substring(1 + par3.indexOf(','), par3.indexOf(')'))
    par3 = this.distance(p1, p2)
  }

  // 6 polygones pleins à construire
  // sommets stockés dans A,B,C,D,E,F,G,H qui sont des tableaux de coordonnées pixels
  const o = this.getObjet(par1)
  const A = this.RenvoiePixels(o.par1, o.par2)
  const B = this.RenvoiePixels(o.par1 + par2, o.par2)
  const E = this.RenvoiePixels(o.par1, o.par2 + par3)
  const F = this.RenvoiePixels(o.par1 + par2, o.par2 + par3)
  const D = this.RenvoiePixels(o.par1 + par4, o.par2 + par5)
  const C = this.RenvoiePixels(o.par1 + par4 + par2, o.par2 + par5)
  const H = this.RenvoiePixels(o.par1 + par4, o.par2 + par3 + par5)
  const G = this.RenvoiePixels(o.par1 + par4 + par2, o.par2 + par3 + par5)
  // face1 et face 2 devant
  const nomForId = this._idCleaner(nom)
  j3pPolygone(this.svg, {
    id: this._getId('id' + nomForId + 'face1'),
    tab: [A, B, F, E],
    couleur: 'none',
    couleurRemplissage: style.couleurdevant,
    opaciteRemplissage: style.opaciteRemplissage / 2
  })
  j3pPolygone(this.svg, {
    id: this._getId('id' + nomForId + 'face2'),
    tab: [B, C, G, F],
    couleur: 'none',
    couleurRemplissage: style.couleurdevant,
    opaciteRemplissage: style.opaciteRemplissage / 2
  })
  j3pPolygone(this.svg, {
    id: this._getId('id' + nomForId + 'face3'),
    tab: [C, G, H, D],
    couleur: 'none',
    couleurRemplissage: style.couleurderriere,
    opaciteRemplissage: style.opaciteRemplissage
  })
  j3pPolygone(this.svg, {
    id: this._getId('id' + nomForId + 'face4'),
    tab: [A, D, H, E],
    couleur: 'none',
    couleurRemplissage: style.couleurderriere,
    opaciteRemplissage: style.opaciteRemplissage
  })
  j3pPolygone(this.svg, {
    id: this._getId('id' + nomForId + 'facedessous'),
    tab: [A, B, C, D],
    couleur: 'none',
    couleurRemplissage: style.couleurdessous,
    opaciteRemplissage: style.opaciteRemplissage
  })
  j3pPolygone(this.svg, {
    id: this._getId('id' + nomForId + 'facedessus'),
    tab: [E, F, G, H],
    couleur: 'none',
    couleurRemplissage: style.couleurdessus,
    opaciteRemplissage: style.opaciteRemplissage
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's1'),
    x1: A[0],
    y1: A[1],
    x2: B[0],
    y2: B[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's2'),
    x1: B[0],
    y1: B[1],
    x2: C[0],
    y2: C[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's3'),
    x1: C[0],
    y1: C[1],
    x2: D[0],
    y2: D[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    pointilles: '5,5'
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's4'),
    x1: D[0],
    y1: D[1],
    x2: A[0],
    y2: A[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    pointilles: '5,5'
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's5'),
    x1: A[0],
    y1: A[1],
    x2: E[0],
    y2: E[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's6'),
    x1: B[0],
    y1: B[1],
    x2: F[0],
    y2: F[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's7'),
    x1: C[0],
    y1: C[1],
    x2: G[0],
    y2: G[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's8'),
    x1: D[0],
    y1: D[1],
    x2: H[0],
    y2: H[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur,
    pointilles: '5,5'
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's9'),
    x1: E[0],
    y1: E[1],
    x2: F[0],
    y2: F[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's10'),
    x1: F[0],
    y1: F[1],
    x2: G[0],
    y2: G[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's11'),
    x1: G[0],
    y1: G[1],
    x2: H[0],
    y2: H[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
  j3pCreeSegment(this.svg, {
    id: this._getId('id' + nomForId + 's11'),
    x1: H[0],
    y1: H[1],
    x2: E[0],
    y2: E[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
}

Repere.prototype.courbe = function courbe (nom, par1, par2, par3, par4, style) {
  const tab = j3pTableauValeurs(par1, par2, par3, par4)
  this.courbe_tabvaleur(nom, tab, style)
}

Repere.prototype.courbe_tabvaleur = function courbeTabvaleur (nom, tabVal, style) {
  // cette fonction construit la courbe lorsqu’on a déjà le tableau de valeurs
  // finalement c’est la deuxième partie de la méthode courbe
  // la ligne suivante sera peut-être utile ?
  // this.construitpoint({nom:'pt1',abscisse:tabVal[0][0],ordonnee:tabVal[0][1],taillepoint:3,taillepolice:16,police:'sans-serif',couleur:style.couleur,epaisseur:style.epaisseur})
  for (let k = 1; k < tabVal.length; k = k + 1) {
    // this.construitpoint({nom:'ptcourbe'+k,abscisse:tab[k][0],ordonnee:tab[k][1],taillepoint:3,taillepolice:16,police:'sans-serif',couleur:style.couleur,epaisseur:style.epaisseur})
    const coordM1 = this.RenvoiePixels(tabVal[k - 1][0], tabVal[k - 1][1])
    const coordM2 = this.RenvoiePixels(tabVal[k][0], tabVal[k][1])
    j3pCreeSegment(this.svg, {
      x1: coordM1[0],
      y1: coordM1[1],
      x2: coordM2[0],
      y2: coordM2[1],
      couleur: style.couleur,
      epaisseur: style.epaisseur
    })
  }
}

/**
 * Trace une droite
 * @param {Object} props
 * @param {string} props.par1 nom du 1er point (qui doit exister)
 * @param {string} props.par2 nom du 2e point (qui doit exister)
 * @param {Object} props.style
 */
Repere.prototype.droite = function droite (props) {
  this._check(props, 'droite')
  const { nom, par1, par2, style, couleur, label } = props
  const o1 = this.getObjet(par1)
  const o2 = this.getObjet(par2)
  if (isNaN(o1.x)) o1.x = o1.par1
  if (isNaN(o1.y)) o1.y = o1.par2
  if (isNaN(o2.x)) o2.x = o2.par1
  if (isNaN(o2.y)) o2.y = o2.par2
  const id = this._getId('droite' + this._idCleaner(par1 + par2))

  if (o1.x === o2.x) {
    const coefY = (this.haut / this.pixelspargraduationY)
    const [x1, y1] = this.RenvoiePixels(o1.x, -2 * coefY)
    const [x2, y2] = this.RenvoiePixels(o1.x, 2 * coefY)
    droite.cd = Number.POSITIVE_INFINITY
    if (typeof style.opacite === 'undefined') {
      j3pCreeSegment(this.svg, {
        id,
        x1,
        y1,
        x2,
        y2,
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      j3pCreeSegment(this.svg, {
        id,
        x1,
        y1,
        x2,
        y2,
        couleur: style.couleur,
        epaisseur: style.epaisseur,
        opacite: style.opacite
      })
    }
  } else {
    const cd = (o2.y - o1.y) / (o2.x - o1.x)

    const oo = o2.y - cd * o2.x
    droite.cd = cd
    const absmin = -2 * (this.larg / this.pixelspargraduationX)
    const absmax = 2 * (this.larg / this.pixelspargraduationX)

    const yy1 = cd * absmin + oo
    const yy2 = cd * absmax + oo
    const [x1, y1] = this.RenvoiePixels(absmin, yy1)
    const [x2, y2] = this.RenvoiePixels(absmax, yy2)
    if (typeof style.opacite === 'undefined') {
      j3pCreeSegment(this.svg, {
        id,
        x1,
        y1,
        x2,
        y2,
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      j3pCreeSegment(this.svg, {
        id,
        x1,
        y1,
        x2,
        y2,
        couleur: style.couleur,
        epaisseur: style.epaisseur,
        opacite: style.opacite
      })
    }
  }

  const minx = -this.xO / this.pixelspargraduationX
  const maxx = (this.larg - this.xO) / this.pixelspargraduationX
  const milieux = (minx + maxx) / 2
  const maxy = this.yO / this.pixelspargraduationY
  const miny = -(this.haut - this.yO) / this.pixelspargraduationY
  const milieuy = (miny + maxy) / 2

  let intersup = this.intersection2(nom, [0, 1, this.yO / this.pixelspargraduationY])
  let dx, dy

  if (style.nommage) {
    if ((intersup[0] <= maxx) && (intersup[0] >= minx)) {
      if (intersup[0] < milieux) {
        dx = 10
        dy = 12
      } else {
        dx = -30
        dy = 12
      }
      intersup = this.RenvoiePixels(intersup[0], intersup[1])
      j3pCreeTexte(this.svg, {
        id: this._getId('nomdroite' + nom),
        x: intersup[0] + dx,
        y: intersup[1] + dy,
        texte: label || '(' + nom + ')',
        taille: 12,
        couleur,
        italique: false,
        fonte: 'sans-serif'
      })
    } else {
      intersup = this.intersection2(nom, [1, 0, (this.larg - this.xO) / this.pixelspargraduationX])
      if (intersup[1] < milieuy) {
        dx = -22
        dy = 0
      } else {
        dx = -22
        dy = 30
      }

      intersup = this.RenvoiePixels(intersup[0], intersup[1])
      j3pCreeTexte(this.svg, {
        id: this._getId('nomdroite' + nom),
        x: intersup[0] + dx,
        y: intersup[1] + dy,
        texte: label || '(' + nom + ')',
        taille: 12,
        couleur: droite.couleur,
        italique: false,
        fonte: 'sans-serif'
      })
    }
  }
} // droite

Repere.prototype.demidroite = function (props) {
  this._check(props, 'demidroite')
  const { nom, par1, par2, style, couleur, label } = props
  const o1 = this.getObjet(par1)
  const o2 = this.getObjet(par2)

  let tab, tab1, tab2
  const id = this._getId('droite' + this._idCleaner(par1 + par2))

  if (o1.x === o2.x) {
    const coefY = (this.haut / this.pixelspargraduationY)
    tab = this.RenvoiePixels(o1.x, o1.y)
    tab2 = this.RenvoiePixels(o1.x, 2 * coefY)
    props.cd = Number.POSITIVE_INFINITY
    if (o2.y < o1.y) tab2[1] = -tab2[1]
    if (typeof style.opacite === 'undefined') {
      j3pCreeSegment(this.svg, {
        id,
        x1: tab[0],
        y1: tab[1],
        x2: tab2[0],
        y2: tab2[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      j3pCreeSegment(this.svg, {
        id,
        x1: tab[0],
        y1: tab[1],
        x2: tab2[0],
        y2: tab2[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur,
        opacite: style.opacite
      })
    }
  } else {
    const cd = (o2.y - o1.y) / (o2.x - o1.x)
    const oo = o2.y - cd * o2.x
    props.cd = cd
    const absmin = -2 * (this.larg / this.pixelspargraduationX)
    const absmax = 2 * (this.larg / this.pixelspargraduationX)

    const yy1 = cd * absmin + oo
    const yy2 = cd * absmax + oo
    const tabmin = this.RenvoiePixels(absmin, yy1)
    const tabmax = this.RenvoiePixels(absmax, yy2)

    tab1 = this.RenvoiePixels(o1.x, o1.y)
    tab2 = this.RenvoiePixels(o2.x, o2.y)
    let tab3
    if (j3pPointDansSegment(tab2, tab1, tabmin)) {
      tab3 = tabmin
    } else {
      tab3 = tabmax
    }
    if (typeof style.opacite === 'undefined') {
      j3pCreeSegment(this.svg, {
        id,
        x1: tab1[0],
        y1: tab1[1],
        x2: tab3[0],
        y2: tab3[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    } else {
      j3pCreeSegment(this.svg, {
        id,
        x1: tab1[0],
        y1: tab1[1],
        x2: tab3[0],
        y2: tab3[1],
        couleur: style.couleur,
        epaisseur: style.epaisseur,
        opacite: style.opacite
      })
    }
  }

  const minx = -this.xO / this.pixelspargraduationX
  const maxx = (this.larg - this.xO) / this.pixelspargraduationX
  const milieux = (minx + maxx) / 2
  const maxy = this.yO / this.pixelspargraduationY
  const miny = -(this.haut - this.yO) / this.pixelspargraduationY
  const milieuy = (miny + maxy) / 2

  let intersup = this.intersection2(nom, [0, 1, this.yO / this.pixelspargraduationY])
  let dx, dy

  if (style.nommage) {
    if ((intersup[0] <= maxx) && (intersup[0] >= minx)) {
      if (intersup[0] < milieux) {
        dx = 10
        dy = 12
      } else {
        dx = -30
        dy = 12
      }
      intersup = this.RenvoiePixels(intersup[0], intersup[1])
      j3pCreeTexte(this.svg, {
        id: this._getId('nomdroite' + nom),
        x: intersup[0] + dx,
        y: intersup[1] + dy,
        texte: label || '(' + nom + ')',
        taille: 12,
        couleur,
        italique: false,
        fonte: 'sans-serif'
      })
    } else {
      intersup = this.intersection2(nom, [1, 0, (this.larg - this.xO) / this.pixelspargraduationX])
      if (intersup[1] < milieuy) {
        dx = -22
        dy = 0
      } else {
        dx = -22
        dy = 30
      }

      intersup = this.RenvoiePixels(intersup[0], intersup[1])
      j3pCreeTexte(this.svg, {
        id: this._getId('nomdroite' + nom),
        x: intersup[0] + dx,
        y: intersup[1] + dy,
        texte: label || '(' + nom + ')',
        taille: 12,
        couleur,
        italique: false,
        fonte: 'sans-serif'
      })
    }
  }
} // demidroite

/**
 * Trace une droite parallèle passant par un point
 * @param {Object} props
 * @param {string} props.par1 Nom du point (qui doit exister dans this.objets)
 * @param {string} props.par2 Le nom de la droite (qui doit exister dans this.objets)
 * @param {Object} style
 */
Repere.prototype.droiteparallele = function droiteparallele (props) {
  this._check(props, 'droiteparallele')
  const { par1, par2, style } = props
  const o1 = this.getObjet(par1)
  const o2 = this.getObjet(par2) // la droite parallèle

  // les coord du point, qui dépendent de pointsur ou pas
  let px, py
  if (o1.type === 'pointsur') {
    px = o1.x
    py = o1.y
  } else {
    px = o1.par1
    py = o1.par2
  }
  const cd = coefdir(this, o2.par1, o2.par2)
  props.cd = cd

  // ce qui dépend du cas vertical ou pas
  let pt1, pt2
  if (cd === Number.POSITIVE_INFINITY) {
    // nom2 est une droite verticale
    const ratio = this.haut / this.pixelspargraduationY
    pt1 = this.RenvoiePixels(px, -2 * ratio)
    pt2 = this.RenvoiePixels(px, 2 * ratio)
  } else {
    const oo = py - cd * px
    const absmin = -(this.larg / this.pixelspargraduationX)
    const absmax = 2 * (this.larg / this.pixelspargraduationX)
    const yy1 = cd * absmin + oo
    const yy2 = cd * absmax + oo
    pt1 = this.RenvoiePixels(absmin, yy1)
    pt2 = this.RenvoiePixels(absmax, yy2)
  }

  // on trace la droite
  j3pCreeSegment(this.svg, {
    id: this._getId('droitepar' + par1 + par2),
    x1: pt1[0],
    y1: pt1[1],
    x2: pt2[0],
    y2: pt2[1],
    couleur: style.couleur,
    epaisseur: style.epaisseur
  })
} // droiteparallele

Repere.prototype.droiteperpendiculaire = function (num, pt1, par1, par2, style) {
  const o1 = this.getObjet(par1)
  if (typeof style.visible !== 'boolean') style.visible = true
  const id = this._getId('droite' + this._idCleaner(par1 + par2))
  // coordonnées de M
  let px1, py1
  if (o1.type === 'pointsur') {
    px1 = o1.x
    py1 = o1.y
  } else {
    px1 = o1.par1
    py1 = o1.par2
  }

  const o2 = this.getObjet(par2) // la droite (d)
  let cd = coefdir(this, o2.par1, o2.par2)

  let oo, absmin, absmax, yy1, yy2
  if (cd === Number.POSITIVE_INFINITY) {
    cd = 0
    oo = py1 - cd * px1
    this.objets[num].cd = cd
    absmin = -(this.larg / this.pixelspargraduationX)
    absmax = (this.larg / this.pixelspargraduationX)

    yy1 = cd * absmin + oo
    yy2 = cd * absmax + oo
    const [x1, y1] = this.RenvoiePixels(absmin, yy1)
    const [x2, y2] = this.RenvoiePixels(absmax, yy2)
    if (style.visible) {
      j3pCreeSegment(this.svg, {
        id,
        x1,
        y1,
        x2,
        y2,
        couleur: style.couleur,
        epaisseur: style.epaisseur
      })
    }
  } else {
    if (!cd) {
      const coefY = (this.haut / this.pixelspargraduationY)
      const [x1, y1] = this.RenvoiePixels(px1, -2 * coefY)
      const [x2, y2] = this.RenvoiePixels(px1, 2 * coefY)
      this.objets[num].cd = Number.POSITIVE_INFINITY
      if (style.visible) {
        j3pCreeSegment(this.svg, {
          id,
          x1,
          y1,
          x2,
          y2,
          couleur: style.couleur,
          epaisseur: style.epaisseur
        })
      }
    } else {
      cd = -1 / cd
      this.objets[num].cd = cd
      oo = py1 - cd * px1
      absmin = -(this.larg / this.pixelspargraduationX)
      absmax = (this.larg / this.pixelspargraduationX)

      yy1 = cd * absmin + oo
      yy2 = cd * absmax + oo
      const [x1, y1] = this.RenvoiePixels(absmin, yy1)
      const [x2, y2] = this.RenvoiePixels(absmax, yy2)
      if (style.visible) {
        j3pCreeSegment(this.svg, {
          id,
          x1,
          y1,
          x2,
          y2,
          couleur: style.couleur,
          epaisseur: style.epaisseur
        })
      }
    }

    const eq1 = this.equation(par2)
    const eq2 = [-eq1[1], eq1[0], -eq1[1] * px1 + eq1[0] * py1]
    const intersection = j3pIntersection(eq1, eq2)
    const distInterPx = Math.sqrt(Math.pow(px1 - intersection[0], 2) + Math.pow(py1 - intersection[1], 2))
    // Il faut faire attention si (px1, py1) et intersection ne sont pas confondus
    // Si c’est le cas, j’utilise le vecteur (eq1, eq2) directeur de par2 (ou normal, je n’en sais rien)
    const normal = (distInterPx < Math.pow(10, -12))
      ? normalise([px1 + eq1[0], py1 + eq1[1]]) // ça marche aussi avec normalise([px1 - eq1[1], py1 + eq1[0]]) semble-t-il... Jene comprends pas pourquoi
      : normalise([px1 - intersection[0], py1 - intersection[1]])
    // 1 pour 30 pixels
    // 1/3 pour 10 pixels

    const rapport = 10 / this.pixelspargraduationX

    normal[0] = rapport * normal[0]
    normal[1] = rapport * normal[1]
    px1 = intersection[0] + normal[0]
    py1 = intersection[1] + normal[1]

    const px3 = -py1 + intersection[1] + intersection[0]
    const py3 = px1 + intersection[1] - intersection[0]
    const anglept1 = this.RenvoiePixels(px1, py1)
    const anglept2 = this.RenvoiePixels(intersection[0], intersection[1])
    const anglept3 = this.RenvoiePixels(px3, py3)
    j3pCreeAngle(this.svg, {
      id: this._getId('angle' + 'droite' + par1 + par2),
      tab1: anglept1,
      tab2: anglept2,
      tab3: anglept3,
      epaisseur: style.epaisseur,
      couleur: style.couleur,
      opacite: 0.2
    })
  }
}

/**
 * Retourne l’objet de repere.objets dont le nom correspond
 * @param {string} nom
 * @return {Object}
 */
Repere.prototype.getObjet = function getObjet (nom) {
  const obj = this.objetsByNom.get(nom)
  if (!obj) console.error(Error(`pas trouvé l’objet ${nom} dans le repere`), this.objets)
  return obj
}

export default Repere