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
}