legacy/core/functionsSvg.js

import { isValidId } from 'src/lib/utils/string'
import { _cssGetFloatAsString, _getTextNode, j3pAngleVecteurs, j3pDistance, j3pElement, j3pEnsureHtmlElement, j3pImageRotation, j3pIsSvgElement, j3pNormeVecteur } from 'src/legacy/core/functions'
import { getStyleFromCssString, setSvgStyle } from 'src/lib/utils/css'

/**
 * Des fonctions génériques pour manipuler du svg
 * @module legacy/core/functions.svg
 */
/**
 * Options génériques pour tout élément svg
 * @typeDef {Object} SvgOptionsFr
 * @property {string} [tag=svg] le tag de l’élément à créer
 * @property {number} [width] largeur de l’élément svg qui sera créé
 * @property {number} [height] hauteur de l’élément svg qui sera créé
 * @property {string} [id] l’id à affecter à cet élément svg
 * @property {Object} [style] Des styles à appliquer à l’élément (à la place de ce qui suit)
 * @property {string} [couleur] couleur du trait (stroke), passer none pour ne pas tracer le contour d’une surface (ou bien epaisseur: 0)
 * @property {number} [epaisseur] épaisseur du trait (strokeWidth)
 * @property {number} [opacite] opacité du trait (strokeOpacity)
 * @property {string} [couleurRemplissage] couleur de remplissage (fill), pour ne pas remplir passer 'none'
 * @property {number|string} [opaciteRemplissage] opacité du remplissage (fillOpacity)
 */

/**
 * Options génériques pour tout élément svg
 * @typeDef {Object} SvgOptions
 * @property {string} [tag=svg] le tag de l’élément à créer
 * @property {number} [width] largeur de l’élément svg qui sera créé
 * @property {number} [height] hauteur de l’élément svg qui sera créé
 * @property {string} [id] l’id à affecter à cet élément svg
 * @property {Object} [style] Des styles à appliquer à l’élément (à la place de ce qui suit)
 * @property {string} [stroke] couleur du trait, pour ne pas tracer le contour d’une surface passer none ou bien strokeWidth: 0
 * @property {number} [strokeWidth] épaisseur du trait (passer 0 pour ne pas avoir de trait)
 * @property {number} [strokeOpacity] opacité du trait
 * @property {string} [fill] couleur de remplissage, pour ne pas remplir passer 'none'
 * @property {number|string} [fillOpacity] opacité du remplissage
 */

/**
 * Le namespace svg
 * @type {string}
 */
export const svgns = 'http://www.w3.org/2000/svg'

/**
 * Normalise les options svg (traduit SvgOptionsFr et cast les types number)
 * Si une propriété existe en fr et en c’est en qui l’emporte
 * @param {Object} options
 * @return {SvgOptions|SvgOptionsFr}
 * @private
 */
export function _j3pNormaliseSvgOptions ({ style, tag, id, width, height, stroke, couleur, strokeOpacity, opacite, strokeWidth, epaisseur, fill, couleurRemplissage, fillOpacity, opaciteRemplissage, pointilles } = {}) {
  if (typeof style === 'string') style = getStyleFromCssString(style)
  const n = {
    tag: tag || 'svg',
    style: typeof style === 'object' ? style : {}
  }
  // attributs
  if (id) n.id = id
  if (width > 0) n.width = Number(width)
  if (height > 0) n.height = Number(height)
  // propriétés de style qu’on prend à la racine de l’objet
  // trait, couleur
  if (stroke) n.style.stroke = stroke
  else if (couleur) n.style.stroke = couleur
  // trait, opacité
  if (strokeOpacity >= 0) n.style.strokeOpacity = _cssGetFloatAsString(strokeOpacity)
  else if (opacite >= 0) n.style.strokeOpacity = _cssGetFloatAsString(opacite)
  // trait, épaisseur
  if (strokeWidth >= 0) n.style.strokeWidth = _cssGetFloatAsString(strokeWidth, 999)
  else if (epaisseur >= 0) n.style.strokeWidth = _cssGetFloatAsString(epaisseur, 999)
  // remplissage, couleur
  if (fill) n.style.fill = fill
  else if (couleurRemplissage) n.style.fill = couleurRemplissage
  // remplissage, opacité
  if (fillOpacity >= 0) n.style.fillOpacity = _cssGetFloatAsString(fillOpacity)
  else if (opaciteRemplissage >= 0) n.style.fillOpacity = _cssGetFloatAsString(opaciteRemplissage)
  // pointillés
  if (pointilles) n.style.strokeDasharray = pointilles
  return n
}

/**
 * Retourne un élément svg avec les options demandées (mais ne l’ajoute pas dans le dom)
 * @param {SvgOptions|SvgOptionsFr} options
 * @return {SVGElement}
 * @private
 */
export function _j3pGetSvgElt (options) {
  options = _j3pNormaliseSvgOptions(options)
  const elt = document.createElementNS(svgns, options.tag || 'svg')
  // id
  if (options.id) {
    if (!j3pElement(options.id, false)) {
      isValidId(options.id)
      elt.setAttribute('id', options.id)
    }
  }
  // taille
  if (options.width) elt.setAttribute('width', options.width)
  if (options.height) elt.setAttribute('height', options.height)
  // css
  if (options.className) elt.setAttribute('class', options.className)
  if (options.style) {
    for (const pCss in options.style) elt.style[pCss] = options.style[pCss]
  }

  return elt
} // j3pCreeSVG

export function j3pCreeAngle (svg, objet) {
  // on normalise les options
  objet.tag = 'path'
  if (!objet.couleurRemplissage) objet.couleurRemplissage = objet.couleur
  if (typeof objet.opacite === 'number' && !objet.opaciteRemplissage) objet.opaciteRemplissage = objet.opacite
  const path = _j3pGetSvgElt(objet)

  const angle = Math.abs(j3pAngleVecteurs(objet.tab2[0] - objet.tab1[0], objet.tab2[1] - objet.tab1[1], objet.tab2[0] - objet.tab3[0], objet.tab2[1] - objet.tab3[1]))

  objet.tab1[0] = Math.round(objet.tab1[0])
  objet.tab1[1] = Math.round(objet.tab1[1])
  objet.tab2[0] = Math.round(objet.tab2[0])
  objet.tab2[1] = Math.round(objet.tab2[1])
  objet.tab3[0] = Math.round(objet.tab3[0])
  objet.tab3[1] = Math.round(objet.tab3[1])

  if (Math.abs(angle - 90) > 0.1) {
    const ymilieu = (objet.tab1[1] + objet.tab3[1]) / 2
    const orientation = (ymilieu > objet.tab2[1]) ? 1 : 0
    const rayon = 3 * Math.round(j3pDistance(objet.tab1[0], objet.tab1[1], objet.tab2[0], objet.tab2[1]))
    let ch = 'M ' + objet.tab2[0] + ' ' + objet.tab2[1] + ' L ' + objet.tab1[0] + ' ' + objet.tab1[1]
    ch += ' A ' + rayon + ',' + rayon + ' 0 0,' + orientation + ' ' + objet.tab3[0] + ',' + objet.tab3[1]
    ch += ' L ' + objet.tab3[0] + ' ' + objet.tab3[1]
    path.setAttribute('d', ch)
    if (typeof svg === 'string') {
      const elementconteneur = j3pElement(svg)
      elementconteneur.appendChild(path)
    } else {
      svg.appendChild(path)
    }
  } else {
    const tab4 = [objet.tab3[0] + objet.tab1[0] - objet.tab2[0], objet.tab3[1] + objet.tab1[1] - objet.tab2[1]]
    const ch = 'M ' + objet.tab2[0] + ' ' + objet.tab2[1] + ' L ' + objet.tab1[0] + ' ' + objet.tab1[1] + ' L ' + tab4[0] + ' ' + tab4[1] + ' L ' + objet.tab3[0] + ' ' + objet.tab3[1]
    path.setAttribute('d', ch)
  }

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(path)

  return path
} // j3pCreeAngle
/**
 * Ajoute un arc dans svg (et le retourne)
 * @param {SVGElement|string} svg
 * @param {SvgOptions|SvgOptionsFr} svgOptions Options génériques du svg avec en plus les params :
 * @param {Object} [arcProps] Si non fourni on lira ces propriétés dans l’objet svgOptions
 * @param {number} arcProps.angleDebut
 * @param {number} arcProps.angle
 * @param {number} arcProps.centreX
 * @param {number} arcProps.centreY
 * @param {number} arcProps.rayon
 * @param {string} [arcProps.id]
 * @return {SVGElement|undefined}
 */
export function j3pCreeArc (svg, svgOptions, arcProps) {
  svgOptions.tag = 'path'
  svgOptions.fill = 'none'
  if (!svgOptions.epaisseur && !svgOptions.strokeWidth) svgOptions.strokeWidth = 1
  const path = _j3pGetSvgElt(svgOptions)
  if (!arcProps) arcProps = svgOptions
  const angle1 = (arcProps.angleDebut / 180) * Math.PI
  const angle2 = (arcProps.angle + arcProps.angleDebut) / 180 * Math.PI
  const ll = (arcProps.angle > 180) ? 1 : 0
  const X0 = arcProps.centreX + arcProps.rayon * Math.cos(angle1)
  const Y0 = arcProps.centreY - arcProps.rayon * Math.sin(angle1)
  const X1 = arcProps.centreX + arcProps.rayon * Math.cos(angle2)
  const Y1 = arcProps.centreY - arcProps.rayon * Math.sin(angle2)
  const R = arcProps.rayon
  // la string pour l’attribut d
  let ch = 'M ' + X0 + ' ' + Y0 + ' '
  ch += ' A ' + R + ' ' + R + ' ' + ' 0 ' + ll + ',0 ' + X1 + ' ' + Y1 // "M "+X0+" "+Y0+
  path.setAttribute('d', ch)
  if (arcProps.id && !j3pElement(arcProps.id, false)) {
    path.setAttribute('id', arcProps.id)
  }
  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(path)
  return path
}

/**
 * Crée un cercle dans le svg
 * @param {SVGElement|string} svg Le svg (ou son id) dans lequel ajouter le cercle
 * @param {Object} options
 * @param {number} options.cx attribut cx du svg créé
 * @param {number} options.cy attribut cy du svg créé
 * @param {number} options.rayon attribut r du svg créé
 * @param {string} [options.id] l’id à affecter au svg qui sera retourné
 * @param {string} [options.couleurRemplissage] si existe le cercle sera rempli avec cette couleur (sinon vide)
 * @param {string} [options.couleur] couleur du trait
 * @param {number|string} [options.epaisseur] épaisseur du trait
 * @param {number|string} [options.opaciteRemplissage] éventuelle propriété fill-opacity
 * @return {SVGCircleElement|undefined}
 */
export function j3pCreeCercle (svg, options) {
  if (!options.couleurRemplissage) options.couleurRemplissage = 'none'
  options.tag = 'circle'
  const circle = _j3pGetSvgElt(options)
  circle.setAttribute('cx', String(options.cx))
  circle.setAttribute('cy', String(options.cy))
  circle.setAttribute('r', String(options.rayon))

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(circle)

  return circle
}

/**
 * Crée une ellipse dans le svg
 * @param {SVGElement|string} svg Le svg (ou son id) dans lequel ajouter le cercle
 * @param {Object} options
 * @param {number} options.cx attribut cx du svg créé
 * @param {number} options.cy attribut cy du svg créé
 * @param {number} options.rayon_x attribut rx
 * @param {number} options.rayon_y attribut ry
 * @param {string} [options.id] l’id à affecter au svg qui sera retourné
 * @param {string} [options.couleurRemplissage] si existe le cercle sera rempli avec cette couleur (sinon vide)
 * @param {string} [options.couleur] couleur du trait
 * @param {number} [options.epaisseur] épaisseur du trait
 * @param {string} [options.opaciteRemplissage] éventuelle propriété fill-opacity
 * @return {SVGElement|undefined}
 */
export function j3pCreeEllipse (svg, options) {
  if (!options.couleurRemplissage) options.couleurRemplissage = 'none'
  options.tag = 'ellipse'
  const ellipse = _j3pGetSvgElt(options)
  ellipse.setAttribute('cx', String(options.cx))
  ellipse.setAttribute('cy', String(options.cy))
  ellipse.setAttribute('rx', String(options.rayon_x))
  ellipse.setAttribute('ry', String(options.rayon_y))

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(ellipse)

  return ellipse
}

/**
 * Ajoute une ligne dans un svg
 * @param {SVGElement|string} svg
 * @param {Object} lineProps
 * @param {number} lineProps.x1
 * @param {number} lineProps.y1
 * @param {number} lineProps.x2
 * @param {number} lineProps.y2
 * @param {string} [lineProps.id]
 * @param {string} [lineProps.couleur]
 * @param {number} [lineProps.epaisseur=0]
 * @param {number} [lineProps.opacite]
 * @param {boolean} [lineProps.pointilles]
 * @return {SVGLineElement|undefined}
 */
export function j3pCreeSegment (svg, lineProps) {
  lineProps.tag = 'line'
  if (isNaN(lineProps.y1)) console.error(Error('j3pCreeSegment récupère NaN pour y1'), lineProps)
  const line = _j3pGetSvgElt(lineProps)
  line.setAttribute('x1', String(lineProps.x1))
  line.setAttribute('y1', String(lineProps.y1))
  line.setAttribute('x2', String(lineProps.x2))
  line.setAttribute('y2', String(lineProps.y2))

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(line)

  return line
}

/**
 * Ajoute un tag g dans le svg (et le retourne)
 * @param {SVGElement} svg
 * @param {string} [id]
 * @return {SVGGElement}
 */
export function j3pCreeGroupe (svg, id) {
  const groupe = document.createElementNS(svgns, 'g')
  if (id) groupe.setAttribute('id', id)
  svg.appendChild(groupe)
  return groupe
}

export function j3pCreePoint (svg, objet) {
  /*
     * Propriétés de l’objet
     * id
     * left,top : coordonnées
     * taille : longueur d’un trait
     * couleur
     * epaisseur
     * nom
     * taillepolice
     *
     */
  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  const g = j3pCreeGroupe(svg, {})

  if (objet.croix !== false) {
    j3pCreeSegment(g, {
      x1: objet.x - objet.taille / 2,
      y1: objet.y - objet.taille / 2,
      x2: objet.x + objet.taille / 2,
      y2: objet.y + objet.taille / 2,
      couleur: objet.couleur,
      epaisseur: objet.epaisseur
    })
    j3pCreeSegment(g, {
      x1: objet.x - objet.taille / 2,
      y1: objet.y + objet.taille / 2,
      x2: objet.x + objet.taille / 2,
      y2: objet.y - objet.taille / 2,
      couleur: objet.couleur,
      epaisseur: objet.epaisseur
    })
  }

  j3pCreeTexte(g, {
    x: objet.decal ? objet.x - 5 + objet.decal[0] - 3 : objet.x - 5,
    y: objet.decal ? objet.y + objet.decal[1] : objet.y - 3,
    texte: objet.nom,
    taille: objet.taillepolice,
    couleur: objet.couleur,
    fonte: 'serif'
  })

  // g a déjà été ajouté au svg par j3pCreeGroupe
  return g
} // j3pCreePoint
/**
 * Crée un rectangle dans le svg (et le retourne)
 * Pour les couleurs c’est du css donc "red" ou "rvb(r, v, b)" ou "#rrvvbb" fonctionnent
 * @param {SVGElement|string} svg Le svg ou son id
 * @param {Object} props
 * @param {string} [props.id]
 * @param {number} props.x
 * @param {number} props.y
 * @param {number} props.width
 * @param {number} props.height
 * @param {number} props.epaisseur (stroke-width)
 * @param {string} [props.couleurRemplissage] (fill)
 * @param {string} [props.opaciteRemplissage] (fill-opacity)
 * @param {string} [props.couleur] couleur du trait (stroke)
 * @return {SVGElement|undefined}
 */
export function j3pCreeRectangle (svg, props) {
  props.tag = 'rect'
  if (!props.couleurRemplissage) props.couleurRemplissage = 'none'
  const rect = _j3pGetSvgElt(props)
  rect.setAttribute('x', String(props.x))
  rect.setAttribute('y', String(props.y))

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(rect)

  return rect
} // j3pCreeRectangle

/**
 * Dessine un secteur angulaire
 * @param {SVGElement} svg
 * @param {Object} options
 * @param {string} [options.id]
 * @param {string} [options.couleur=#000]
 * @param {number|string} [options.opacite=1]
 * @param {number} options.angleDebut
 * @param {number} options.anglefin
 * @param {number} options.centreX
 * @param {number} options.centreY
 * @param {number} options.rayon
 */
export function j3pCreeSecteur (svg, { id, couleur, opacite, angleDebut, anglefin, centreX, centreY, rayon }) {
  const options = {
    tag: 'path',
    stroke: couleur || '#000',
    strokeOpacity: opacite || 1
  }
  if (id) options.id = id
  const path = _j3pGetSvgElt(options)
  const angle1 = (angleDebut / 180) * Math.PI
  const angle2 = (anglefin / 180) * Math.PI
  const X0 = centreX + rayon * Math.cos(angle1)
  const Y0 = centreY - rayon * Math.sin(angle1)
  const X1 = centreX + rayon * Math.cos(angle2)
  const Y1 = centreY - rayon * Math.sin(angle2)
  const R = rayon
  const xO = centreX
  const yO = centreY
  let ch = 'M ' + xO + ' ' + yO + ' L ' + X0 + ' ' + Y0 + ' '
  ch += ' A ' + R + ' ' + R + ' ' + ' 0 0,0 ' + X1 + ' ' + Y1// "M "+X0+" "+Y0+
  ch += 'L ' + xO + ' ' + yO
  path.setAttribute('d', ch)

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(path)

  return path
}

/**
 * Ajoute un svg dans un conteneur html (utiliser j3pSvgAppend pour ajouter un svg dans un svg)
 * @param {HTMLElement|string} container Le conteneur html ou son id
 * @param {Object} svgProps
 * @param {number} svgProps.width
 * @param {number} svgProps.height
 * @param {string|number[]} [svgProps.viewBox]
 * @param {string} [svgProps.preserveAspectRatio] none|x(Mid|Min|Max)Y(Mid|Min|Max)
 * @param {string} [svgProps.tag=svg] le tag du svg
 * @param {string} [svgProps.id] id du svg
 * @param {string|Object} [svgProps.style] styles éventuels
 * @return {SVGElement}
 * @throws {Error} si le conteneur n’existe pas
 */
export function j3pCreeSVG (container, svgProps) {
  container = j3pEnsureHtmlElement(container)
  const svg = document.createElementNS(svgns, 'svg')
  if (svgProps.id) svg.setAttribute('id', svgProps.id)
  svg.setAttribute('width', svgProps.width)
  svg.setAttribute('height', svgProps.height)
  // viewBox
  if (svgProps.viewBox) {
    if (Array.isArray(svgProps.viewBox) && svgProps.viewBox.length === 4) {
      svgProps.viewBox = svgProps.viewBox.join(' ')
    }
    if (typeof svgProps.viewBox === 'string' && /^[0-9.-]+ [0-9.-]+ [0-9.]+ [0-9.]+$/.test(svgProps.viewBox)) {
      svg.setAttribute('viewBox', svgProps.viewBox)
    } else {
      console.error(Error('viewBox invalide'), svgProps.viewBox)
    }
  }
  // svgProps.preserveAspectRatio
  if (svgProps.preserveAspectRatio) {
    if (
      typeof svgProps.preserveAspectRatio === 'string' &&
      (
        svgProps.preserveAspectRatio === 'none' ||
        /^x(Mid|Min|Max)Y(Mid|Min|Max)$/.test(svgProps.preserveAspectRatio)
      )
    ) {
      svg.setAttribute('preserveAspectRatio', svgProps.preserveAspectRatio)
    } else {
      console.error(Error('preserveAspectRatio invalide'), svgProps.preserveAspectRatio)
    }
  }
  // style
  if (svgProps.style) {
    for (const pCss in svgProps.style) svg.style[pCss] = svgProps.style[pCss]
  }
  container.appendChild(svg)
  return svg
} // j3pCreeSVG

export function j3pCreeVecteur (svg, objet) {
  // objet= {numero,id,x1,y1,x2,y2,couleur,epaisseur,opacite,pointilles}
  // numero : numéro de la condition
  // couleur : chaine comme "red" ou "rgb(x,y,z)" x<255 ou
  // opacite : chaine comprise entre 0, 0.1 etc jusque "1.0"
  // epaisseur : entier
  // id
  // optionnel :
  const groupe = document.createElementNS(svgns, 'g')
  groupe.setAttribute('id', objet.id)

  const line = document.createElementNS(svgns, 'line')
  line.setAttribute('id', objet.id + 'line')
  line.setAttribute('x1', objet.x1)
  line.setAttribute('y1', objet.y1)
  line.setAttribute('x2', objet.x2)
  line.setAttribute('y2', objet.y2)
  let style = ''
  if (objet.couleur) style += 'stroke:' + objet.couleur + ';'
  if (objet.epaisseur) style += 'stroke-width:' + objet.epaisseur + ';'
  if (objet.opacite) style += 'stroke-opacity:' + objet.opacite + ';'
  const style2 = style
  if (objet.pointilles) {
    style += 'stroke-dasharray:' + objet.pointilles + ';'
  }

  line.setAttribute('style', style)
  const x1 = objet.x1
  const y1 = objet.y1
  const x2 = objet.x2
  const y2 = objet.y2
  const im3 = j3pImageRotation(x2, y2, 25, x1, y1)
  const im4 = j3pImageRotation(x2, y2, -25, x1, y1)

  const x3 = x2 + (15 / j3pNormeVecteur(im3[0] - x2, im3[1] - y2)) * (im3[0] - x2)
  const y3 = y2 + (15 / j3pNormeVecteur(im3[0] - x2, im3[1] - y2)) * (im3[1] - y2)
  const x4 = x2 + (15 / j3pNormeVecteur(im4[0] - x2, im4[1] - y2)) * (im4[0] - x2)
  const y4 = y2 + (15 / j3pNormeVecteur(im4[0] - x2, im4[1] - y2)) * (im4[1] - y2)

  const line2 = document.createElementNS(svgns, 'line')
  line2.setAttribute('x1', x3)
  line2.setAttribute('y1', y3)
  line2.setAttribute('x2', x2)
  line2.setAttribute('y2', y2)
  line2.setAttribute('style', style2)
  const line3 = document.createElementNS(svgns, 'line')
  line3.setAttribute('x1', x4)
  line3.setAttribute('y1', y4)
  line3.setAttribute('x2', x2)
  line3.setAttribute('y2', y2)
  line3.setAttribute('style', style2)

  groupe.appendChild(line)
  groupe.appendChild(line2)
  groupe.appendChild(line3)

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(groupe)

  return groupe
} // j3pCreeVecteur
export function j3pCreeVirgule (svg, objet) {
  /*
     *  objet
     *  {id,top,left,rayon,couleur,couleurRemplissage,hauteur,angle}
     *
     */
  j3pCreeCercle(svg, {
    cx: objet.left,
    cy: objet.top,
    rayon: objet.rayon,
    couleur: objet.couleur,
    couleurRemplissage: objet.couleurRemplissage
  })
  const tab = []
  tab[0] = [objet.left + objet.rayon - 1, objet.top]
  tab[1] = [objet.left + objet.rayon + 3, objet.top + objet.hauteur / 2]
  tab[2] = [objet.left - 4, objet.top + objet.hauteur]

  const path = document.createElementNS(svgns, 'path')
  const ch = 'M' + tab[0][0] + ' ' + tab[0][1] + ' Q ' + tab[1][0] + ' ' + tab[1][1] + ' , ' + tab[2][0] + ' ' + tab[2][1]
  path.setAttribute('d', ch)
  const style = 'stroke:' + objet.couleur + ';stroke-width:1;fill:' + objet.couleur + ';'
  path.setAttribute('style', style)

  svg.appendChild(path)
} // j3pCreeVirgule
/**
 * Ajoute du texte dans un svg
 * @param {SVGElement|string} svg Le svg conteneur ou son id
 * @param {Object} txtProps
 * @param {string} txtProps.texte Le texte à insérer
 * @param {string} [txtProps.id]
 * @param {string} [txtProps.couleur]
 * @param {string} [txtProps.fonte]
 * @param {number} txtProps.x
 * @param {number} txtProps.y
 * @param {number} txtProps.taille (sera mis en font-size, ATTENTION en pt !)
 * @param {number} txtProps.
 * @param {Object} [txtProps.rotation]
 * @param {number} txtProps.rotation.angle
 * @param {number} txtProps.rotation.Cx
 * @param {number} txtProps.rotation.Cy
 * @param {boolean} [txtProps.italique=false]
 * @param {boolean} [txtProps.bold=false]
 * @param {boolean} [txtProps.centerX=false] Passer true pour centrer le texte sur son x
 * @param {boolean} [txtProps.centerY=false] Passer true pour centrer le texte sur son y
 * @return {SVGTextElement}
 */
export function j3pCreeTexte (svg, txtProps) {
  const text = document.createElementNS(svgns, 'text')
  if (txtProps.id) text.setAttribute('id', txtProps.id)
  text.setAttribute('x', txtProps.x)
  text.setAttribute('y', txtProps.y)
  if (txtProps.rotation) {
    text.setAttribute('transform', 'rotate(' + txtProps.rotation.angle + ' ' + txtProps.rotation.Cx + ',' + txtProps.rotation.Cy + ')')
  }
  if (txtProps.couleur) text.style.fill = txtProps.couleur
  if (txtProps.italique) text.style.fontStyle = 'italic'
  if (txtProps.taille) text.style.fontSize = txtProps.taille + 'pt'
  if (txtProps.fonte) text.style.fontFamily = txtProps.fonte
  if (txtProps.bold) text.style.fontWeight = 'bold'

  // on ajoute le contenu
  text.appendChild(_getTextNode(txtProps.texte))

  // et on insère tout ça dans le svg
  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(text)
  // on centre si on nous le demande
  if (txtProps.centerX) {
    // rect = text.getBoundingClientRect()
    // text.setAttribute('x', text.getAttribute('x') - rect.width / 2)
    text.style.textAnchor = 'middle'
  }
  if (txtProps.centerY) {
    // if (!rect) rect = text.getBoundingClientRect()
    // text.setAttribute('y', text.getAttribute('y') - rect.height / 2)
    // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline
    text.style.dominantBaseline = 'middle'
  }

  return text
} // j3pCreeTexte
export function j3pGradueSegment (svg, objet) {
  // objet.style = {type:1,nb:3,couleur:"#000",longueur:10,epaisseur:2,opacite:0.5}
  // nb = nombre d’intervalles
  // type = 1 ==> graduations à droite

  let normal = [objet.y1 - objet.y2, objet.x2 - objet.x1]
  if (objet.style.type === 1) {
    const norme = Math.sqrt(normal[0] * normal[0] + normal[1] * normal[1])
    normal = [normal[0] / norme * objet.style.longueur, normal[1] / norme * objet.style.longueur]
  }

  const groupe = j3pCreeGroupe(svg, objet.id)

  for (let k = 1; k < objet.style.nb; k++) {
    const ab = objet.x1 + (k / objet.style.nb) * (objet.x2 - objet.x1)
    const or = objet.y1 + (k / objet.style.nb) * (objet.y2 - objet.y1)
    j3pCreeSegment(groupe, {
      x1: ab,
      y1: or,
      x2: ab + normal[0],
      y2: or + normal[1],
      couleur: objet.style.couleur,
      epaisseur: objet.style.epaisseur,
      opacite: objet.style.opacite
    })
  }

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(groupe)
  return groupe
}

/**
 * Ajoute un polygone dans le svg (ne pas mettre de couleur de remplissage pour ne pas le remplir)
 * @param {SVGElement} svg
 * @param {Object} pathProps
 * @param {string} [pathProps.id]
 * @param {string} pathProps.couleurRemplissage
 * @param {string} pathProps.couleur
 * @param {Array[]} pathProps.tab La liste des sommets, chacun étant un Array [x, y]
 * @param {number} pathProps.opaciteRemplissage
 * @return {SVGPathElement|undefined}
 */
export function j3pPolygone (svg, pathProps) {
  // pathProps= {id,tab,couleur,couleurRemplissage,opaciteRemplissage}
  // tab = [[xi,yi],....]
  const path = document.createElementNS(svgns, 'path')
  if (pathProps.id) path.setAttribute('id', pathProps.id)
  // construction de la chaine du path
  let ch = ''
  for (let i = 0; i < pathProps.tab.length; i++) {
    ch += i === 0 ? 'M' : 'L'
    ch += pathProps.tab[i][0] + ' ' + pathProps.tab[i][1]
  }
  // et on ferme le path en retournant sur le 1er point
  ch += 'L' + pathProps.tab[0][0] + ' ' + pathProps.tab[0][1]
  path.setAttribute('d', ch)

  path.setAttribute('fill', pathProps.couleurRemplissage)
  path.setAttribute('stroke', pathProps.couleur)
  path.setAttribute('fill-opacity', pathProps.opaciteRemplissage)
  path.setAttribute('stroke-width', pathProps.epaisseur)

  if (typeof svg === 'string') svg = j3pElement(svg)
  if (!j3pIsSvgElement(svg, true)) return
  svg.appendChild(path)
  return path
}

/**
 * Ajoute un élément dans un svg
 * @param {SVGElement} svgElt (pourrait être un HTMLElement si tag est 'svg', pour ajouter un svg dans le dom html)
 * @param {string} tag le tag svg à insérer dans svgElt
 * @param {Object} attrs Les attributs à ajouter au tag (la propriété xlink sera ajoutée avec son namespace)
 * @param {Object} [options]
 * @param {Object} [options.style] le style sous forme d’objet (peut aussi être passé en string dans attrs)
 * @param {string} [options.text] Le texte à ajouter dans le tag (à priori pour un tag text), ce sera un TextNode (avec conversion des entités html éventuelles &xxx;)
 * @param {string} [options.bgColor] Une couleur de fond à mettre sous l’élément (à priori du texte), ça va ajouter un tag rect coloré avec cette couleur sous l’élément
 * @param {number} [options.bgPadding=0] Le débordement du rectangle de fond à ajouter, par rapport au tag inséré, en unités du svg
 * @return {SVGElement}
 */
export function j3pSvgAppend (svgElt, tag, attrs = {}, { style, text, bgColor, bgPadding } = {}) {
  const el = document.createElementNS(svgns, tag)
  // les attributs
  for (const [key, value] of Object.entries(attrs)) {
    if (key === 'xlink') {
      el.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', value)
    } else {
      el.setAttribute(key, value)
    }
  }
  if (text) el.appendChild(_getTextNode(text))
  if (style) setSvgStyle(el, style)
  svgElt.appendChild(el)
  if (bgColor) {
    let { x, y, width, height } = el.getBBox()
    if (Number.isInteger(bgPadding) && bgPadding > 0) {
      x -= bgPadding
      y -= bgPadding
      width += 2 * bgPadding
      height += 2 * bgPadding
    }
    j3pSvgAppend(svgElt, 'rect', { x, y, height, width }, { style: { fill: bgColor, strokeWidth: 0 } })
    // et faut remettre le texte après dans le dom pour qu’il soit au-dessus
    svgElt.appendChild(el)
  }
  return el
}