/*!
* MathGraph32 Javascript : Software for animating online dynamic mathematics figures
* https://www.mathgraph32.org/
* @Author Yves Biton (yves.biton@sesamath.net)
* @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
* Version 8.3.2
*/
import constantes from 'src/kernel/constantes'
export const languages = ['en', 'es', 'fr']
const defaultLanguage = 'fr'
let lastLanguageChoosen
const svgns = 'http://www.w3.org/2000/svg'
export const mimeType = 'application-x/MathGraph32'
export const mtgFileExtension = 'mgj'
/**
* Retourne l'url de mathgraph (local|dev|prod suivant contexte), avec slash de fin
* @returns {string}
*/
export function getBaseUrl () {
if (window.mtgUrl) {
if (!window.mtgUrl.endsWith('/')) window.mtgUrl += '/'
return window.mtgUrl
}
if (window.location.host === 'localhost:8082') return 'http://localhost:8082/'
if (/\.sesamath.dev$/.test(window.location.hostname)) return 'https://dev.mathgraph32.org/js/'
return 'https://www.mathgraph32.org/js/'
}
/**
* Retourne l'url de base de MathJax (qui se termine par MathJax3/)
* @returns {string}
*/
export function getMathjaxBase () {
if (window.mathjax3Base) return window.mathjax3Base
if (window.location.hostname === 'localhost' || /\.sesamath.dev$/.test(window.location.hostname)) {
return 'https://dev.mathgraph32.org/js/MathJax3/'
}
return 'https://www.mathgraph32.org/js/MathJax3/'
}
const _digit64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+#'
const _digit64Standard = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
// Ajout version 5.0.3 pour modification de testToile
const SHORT_MIN_VALUE = -32768
export const SHORT_MAX_VALUE = +32767
// DataInputStream.numeroVersionActif = 17; // Changé version 5.4
// DataInputStream.numeroVersionActif = 18 // Changé version 6.0
// déplacé ici pour éviter un pb de dépendance cyclique
// export const version = 19 // Changé version 6.6 car maintenant chaque élément a un tag (chaîne vide par défaut)
// export const version = 20 // Version 8.0.0 : On change le numéro de version car tous les objets graphiques ont maintenant une transparence
export const version = 21 // Version 8.1.1 : Les affichages peuvent maintenant être punaisés
export const ConvDegRad = Math.PI / 180
export const ConvRadDeg = 180 / Math.PI
/**
* @typedef KernelUniteAngle
* @type {(0|1|'radian'|'degre')}
*/
/**
* Constante pour l'unité radian
* @type {KernelUniteAngle}
*/
export const uniteAngleRadian = 0
export const uniteAngleDegre = 1
export const erreurCalculException = 'Erreur de calcul'
export const NombreMaxiTermesSuiteRec = 100000
export const MIN_VALUE = -2147483648
export const MAX_VALUE = 2147483647
export const cos30 = Math.cos(30 * ConvDegRad)
export const sin30 = 0.5
// Ajout version mtgApp pour gérer les écarns tactiles ou non //
export const distMinForTouch = 20
export const distMinForMouse = 10
/**
* @typedef {Object} TagAttributes
* @type {Object}
* @enum {string}
*/
// @todo déplacer cens et ce dans dom.js
/**
* Créer un élément name dans le namespace http://www.w3.org/2000/svg
* @param {string} name tag à créer
* @param {TagAttributes} att ses attributs
* @returns {SVGElement}
*/
export function cens (name, att) {
const element = document.createElementNS(svgns, name)
if (att) {
for (const [key, value] of Object.entries(att)) {
element.setAttribute(key, value)
}
}
/* Supprimé. Inutile (avait été ajouté version MathJax 3)
if (name === "svg") {
element.setAttribute("xmlns", "http://www.w3.org/2000/svg");
element.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
}
*/
return element
}
/**
* Crée un élément de tag name avec les attributs de l'objet att passés en paramètre
* et le retourne
* @param {string} name
* @param {Object} [att]
* @returns {Element}
*/
export function ce (name, att) {
const element = document.createElement(name)
if (att) {
for (const [key, value] of Object.entries(att)) {
element.setAttribute(key, value)
}
}
return element
}
/**
* Retourne le code langue (à 2 lettres) préféré du navigateur, s'il y en a un dans notre liste, fr sinon
* @param {string} [languageWanted] la langue demandée, si valide elle sera mémorisée et retournée par les appels suivants
* @returns {string}
*/
export function getLanguage (languageWanted) {
// si on nous avait déjà demandé une langue géré, on reste dessus
if (lastLanguageChoosen) return lastLanguageChoosen
if (languages.includes(languageWanted)) {
// si on nous passe une demande valide, on la mémorise
lastLanguageChoosen = languageWanted
return languageWanted
}
try {
// on prend le premier qui match notre liste, et le prochain appel fera la même chose
const lang = window.navigator.languages.find(language => languages.some(l => language.startsWith(l)))
if (lang) return lang.substring(0, 2)
} catch (error) {
// on ignore l'erreur des vieux navigateurs qui n'implémentent pas navigator.languages
}
return defaultLanguage
}
/**
* Ensemble des textes de l'appli dans la langue demandée au chargement (sinon celle préférée du navigateur).
* Sera peuplé par loadTextes d'après la langue choisie (une seule dans le dom, le dernier appel écrase les précédents).
* @private
* @type {Object}
*/
export const textes = {}
/**
* Renvoie la chaine correspondante au textCode suivant la langue préférée du navigateur.
* @param {string} textCode le "code" du texte, propriété des objets exportés par les différentes src/textes/*.js
* @param {Object} [options]
* @param {Object} [options.lax=false] passer true pour que ça ne râle pas en console si textCode ne correspond pas à une clé connue de textes
* @returns {string}
*/
export function getStr (textCode, { lax = false } = {}) {
// on retourne une string vide sans râler si on nous passe pas de textCode
if (!textCode) return ''
const txt = textes[textCode] || ''
if (!txt && !lax) console.warn(Error(`Le texte ${textCode} n’existe pas`), textes)
return txt
}
/**
* Fonction renvoyant true si la valeur absolue de x est inférieure à 10^-9
* @param {number} x
* @returns {boolean}
*/
export function zero (x) {
return (Math.abs(x) < 1e-9)
}
/**
* Fonction renvoyant true si la valeur absolue de x est inférieure à 10^-11
* @param {number} x
* @returns {boolean}
*/
export function zero11 (x) {
return (Math.abs(x) < 1e-11)
}
/**
* Fonction renvoyant true si la valeur absolue de x est inférieure à 10^-11
* @param {number} x
* @returns {boolean}
*/
export function zero13 (x) {
return (Math.abs(x) < 1e-13)
}
// Modifié version 5.1 pour tenir compte d'une écriture scientifique
/**
* Renvoie une chaîne de caractères reprséentant le nombre number avec
* digits décimales.
* @param {number} number
* @param {number} digits
* @param {boolean} decimalDot passer à false pour que le séparateur décimal des affichages soit la virgule
* @returns {string}
*/
export function chaineNombre (number, digits, decimalDot = true) {
let ind, exp, nb
if (number === 0) return '0'
if (number === Number.NEGATIVE_INFINITY) return '-∞'
if (number === Number.POSITIVE_INFINITY) return '∞'
let ch = number.toFixed(digits)
const indexp = ch.indexOf('e')
if (indexp === -1) exp = ''
else {
exp = ch.substring(indexp)
ch = ch.substring(0, indexp)
nb = parseFloat(ch)
ch = nb.toFixed(digits)
}
let i = ch.length - 1
if ((ind = ch.indexOf('.', 0)) !== -1) {
while ((i >= ind) && (ch.charAt(i) === '0' || ch.charAt(i) === '.')) i--
ch = ch.substring(0, i + 1)
}
if (!decimalDot) ch = ch.replaceAll('.', ',')
if (ch === '-0') return '0' + exp
// else return ch; // Modification version 4.9.2. On rajoute un espace si le nombre est négatif après le signe -
if (ch.charAt(0) === '-') return '- ' + ch.substring(1) + exp
else return ch + exp
}
/**
* Fonction renvoyant une chaîne de caractères codant en base 64 le tableau de bytes ba.
* Si la dimension du tableau n'est pas un multiple de 3, un ou deux zéros lui sont ajoutés et la chaîne
* de caractères se termine par un ou deux caractères =
* @param {string[]} ba Un tableau de caractères qui représentent des bytes
* @param {boolean} bstandard Si true on encode de façon standard sinon avec encodage mathGraph32 avec / remplacé par #
* @returns {string}
*/
export function base64Encode (ba, bstandard) {
// Il faut que la dimension du tableau de bytes soit un multiple de 3 (3 bytes seront codés sur 4 caractères)
// A noter que un ou deux bytes mis à zéro de plus seront peut-être codés. Pour chacun on mettra un signe = à la fin
// de la chaîne de caractères
const digit = bstandard ? _digit64Standard : _digit64
const len = ba.length
const r = len % 3
const q = Math.floor((len - 1) / 3)
const q2 = q + 1
if (r !== 0) {
const ba2 = new Array(q2 * 3)
for (let j = q * 3 + r; j < q2 * 3; j++) ba2[j] = 0
for (let k = 0; k < len; k++) ba2[k] = ba[k]
ba = ba2
}
let ch = ''
for (let i = 0; i < q2 * 3; i += 3) {
const n = ((ba[i] & 0xff) << 16) + ((ba[i + 1] & 0xff) << 8) + (ba[i + 2] & 0xff) // Les poids forts en premier
ch += digit.charAt((n >> 18) & 0x3f)
ch += digit.charAt((n >> 12) & 0x3f)
ch += digit.charAt((n >> 6) & 0x3f)
ch += digit.charAt(n & 0x3f)
}
// Revu version mtgApp
// var paddingCount = Math.floor((3 - r) % 3); // nul si r = 0 // 1 si r = 2 et 2 si r = 3 c'est le nombre de = à mettre à la fin
const paddingCount = (3 - r) % 3 // nul si r = 0 // 1 si r = 2 et 2 si r = 3 c'est le nombre de = à mettre à la fin
return ch.substring(0, (q + 1) * 4 - paddingCount) + '=='.substring(0, paddingCount)
}
/**
* Fonction renvoyant un tableau d'entiers représentant des bytes d'un flux de bytes.
* @param {string} b64String une chaîne base64.
* @param {boolean} bstandard Si true on décode de façon standard sinon avec encodage mathGraph32 avec / remplacé par #
* @returns {number[]} Tableau de bytes.
*/
export function base64Decode (b64String, bstandard) {
const digit = arguments.length === 1 ? _digit64 : (bstandard ? _digit64Standard : _digit64)
let paddingCount = 0
const len = b64String.length
const indegal = b64String.indexOf('=')
if (indegal !== -1) {
if (b64String.charAt(len - 1) === '=') {
if (b64String.charAt(len - 2) === '=') paddingCount = 2
else paddingCount = 1
}
b64String = b64String.substring(0, indegal) + 'AA'.substring(2 - paddingCount) // On remplace les = par des An donc des zéros
} else paddingCount = 0
// Ligne suivante modifiée JavaScript
const barray = new Array((Math.floor((b64String.length - 1) / 4) + 1) * 3 - paddingCount)
const der = len - paddingCount
let j = 0
for (let i = 0; i < len; i += 4) {
const car1 = b64String.charAt(i)
const car2 = b64String.charAt(i + 1)
const car3 = b64String.charAt(i + 2)
const car4 = b64String.charAt(i + 3)
const n = (digit.indexOf(car1) << 18) + (digit.indexOf(car2) << 12) +
(digit.indexOf(car3) << 6) + digit.indexOf(car4)
if (i + 3 < der) barray[j + 2] = (n & 0xff)
if (i + 2 < der) barray[j + 1] = ((n >> 8) & 0xff)
barray[j] = ((n >> 16) & 0xff)
j += 3
}
return barray
}
/**
* Fonction renvoyant le PGCD de a et de b.
* Avant appel a et b doivent être entiers et non tous les deux nuls, positifs ou nuls
* @param {number} a Entier positif
* @param {number} b Entier positif
* @returns {number}
*/
export function pgcd (a, b) {
while (b !== 0) {
const r = a % b
a = b
b = r
}
return a
}
/**
* Fonction renvoyant le PPCM de a et de b
* Avant appel a et b doivent être entiers et non tous les deux nuls, positifs ou nuls
* @param {number} a
* @param {number} b
* @returns {number}
*/
export function ppcm (a, b) {
if ((a === 0) || (b === 0)) return 0
const q = a / pgcd(a, b)
// Modifié version 7.3 pour optimisation
// return Math.floor(q * b + 0.5)
return Math.round(q * b)
}
/**
* Fonction renvoyant le coefficient du binôme Cnp
* Avant appel n et p doivent être entiers, n > 0,
* et p compris entre 0 et n
* @param {number} n
* @param {number} p
* @returns {number}
*/
export function ncr (n, p) {
if (p > n / 2) p = n - p
if (p === 0) return 1
let d = n / p
const p1 = p
for (let k = 1; k < p1; k++) {
p--
n--
const d1 = n / p
d = d * d1
}
// Modifié version 7.3 pour optimisation
// return Math.floor(d + 0.5)
return Math.round(d)
}
/**
* Fonction renvoyant le nombre de permutations à p éléments d'un ensemble à n éléments
* Avant appel n et p doivent être entiers, n > 0.
* @param {number} n
* @param {number} p
* @returns {number}
*/
export function npr (n, p) {
if (p === 0) return 1
let d = n
for (let k = 1; k < p; k++) {
n--
d = d * n
}
return d
}
/**
* Fonction renvoyant true si un angle de mesure r est considéré comme
* presque nul (valeur absolue inféieure à 10^-7)
* @param {number} r
* @returns {boolean}
*/
// Version 8.2 : on passe à 1e-7 car sinon certains arcs ne sont pas bien tracés
export function zeroAngle (r) {
// return (Math.abs(r) < 1e-7)
return (Math.abs(r) < 1e-5)
}
/**
* Renvoie la mesure principale en radians d'un angle de mesure angrad.
* @param {number} angrad
* @returns {number}
*/
// Version 8.2 : On a lodifié zeroAngle en remplaçant 1e-7 par 1e-5
// mais ici on garde la comparaison à 1e-7 près
export function mesurePrincipale (angrad) {
if (Math.abs(Math.abs(angrad) - Math.PI) < 1e-7) return Math.PI
const dbpi = 2.0 * Math.PI
return angrad - dbpi * Math.floor((angrad + Math.PI) / dbpi)
}
/**
* Fonction renvoyant true si x et y sont des nombres compris entre le plus
* petit et le plus grand entiers représentés sur 4 actets.
* @param {number} x
* @param {number} y
* @returns {boolean}
*/
export function testToile (x, y) {
return ((x <= SHORT_MAX_VALUE) && (y <= SHORT_MAX_VALUE) &&
(x >= SHORT_MIN_VALUE) && (y >= SHORT_MIN_VALUE))
}
/**
* Renvoie true si les deu vecteurs e et v sont considérés comme colinéiares
* (à epsilon près).
* @param {Vect} u
* @param {Vect} v
* @returns {boolean}
*/
export function colineaires (u, v) {
const n1 = u.norme()
const n2 = v.norme()
if (zero(n1) || zero(n2)) return true
else return (zero((u.x * v.y - u.y * v.x) / n1 / n2))
}
/**
* Renvoie true si les deu vecteurs e et v sont considérés comme colinéiares
* et de même sens (à epsilon près).
* @param {Vect} u
* @param {Vect} v
* @returns {boolean}
*/
export function colineairesMemeSens (u, v) {
return colineaires(u, v) && (u.x * v.x + u.y * v.y >= 0)
}
/**
* Renvoie la distance entre deux points de coordonnées (x1,y1) et (x2,y2).
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @returns {number}
*/
export function distancePointPoint (x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
}
/**
* Renvoie le carré de la distance entre deux points de coordonnées (x1,y1) et (x2,y2).
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @returns {number}
*/
export function distancePointPointCarre (x1, y1, x2, y2) {
return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
}
/**
* Renvoie dans res.x et res.y les coordonnées de l'intersection de deux droites sécantes
* On sait qu'elles sont sécantes avant appel.
* @param {number} p1x Abscisse d'un point de la première droite
* @param {number} p1y Ordonnée d'un point de la première droite
* @param {Vect} u1 Vecteur directeur de la première droite.
* @param {number} p2x Abscisse d'un point de la deuxième droite
* @param {number} p2y Ordonnée d'un point de la deuxième droite
* @param {Vect} u2 Vecteur directeur de la deuxième droite.
* @param {Object} res
* @returns {void}
*/
export function intersectionDroitesSecantes (p1x, p1y, u1, p2x, p2y, u2, res) {
const d = u1.x * u2.y - u2.x * u1.y
const k1 = u1.y * p1x - u1.x * p1y
const k2 = u2.y * p2x - u2.x * p2y
res.x = (u1.x * k2 - u2.x * k1) / d
res.y = (u1.y * k2 - u2.y * k1) / d
}
/**
* Fonction renvoyant dans point.x l'indice de la valeur maximale contenue dans le tableau
* de nombres tab et dans y l'indice de la valeur maximale.
* @param {number} c Le nombre d'éléments du tableau.
* @param {number[]} tab Un tableau de nombres
* @param {Object} point
* @returns {void}
*/
export function indiceMiniMaxi (c, tab, point) {
let imin, imax, min, max
min = tab[0]
max = min
imin = 0
imax = 0
for (let i = 1; i <= c; i++) {
if (tab[i] < min) {
imin = i
min = tab[i]
}
if (tab[i] > max) {
imax = i
max = tab[i]
}
}
point.x = imin
point.y = imax
}
/**
* Renvoie true si la différence entre angle1 et angle2 est en valeur absolue
* proche de pi/2 ou 3pi/2
* @param {number} angle1
* @param {number} angle2
* @returns {boolean}
*/
export function testAngleDroit (angle1, angle2) {
const dif = Math.abs(angle2 - angle1)
return zeroAngle(dif - Math.PI / 2) || zeroAngle(dif - 3 * Math.PI / 2)
}
/**
* Renvoie les coordonnées du pointeur souris de l'événement event relatives au svg par
* @param {MouseEvent} event
* @param {SVGElement} par
* @param {number} decx Valeur à retrancher au premier élément du tableau de retour
* @param {number} decy Valeur à retrancher au premier élément du tableau de retour
* @returns {Point}
*/
export function getMousePositionToParent (event, par, decx = 0, decy = 0) {
const { left, top } = par.getBoundingClientRect()
return {
x: event.clientX - left - decx,
y: event.clientY - top - decy
}
}
/**
* Renvoie les coordonnées du pointeur d'un périphérique mobile de l'événement event par rapport au svg par
* @param {TouchEvent} event
* @param {SVGElement} parent
* @param {number} decx Valeur à retrancher au premier élément du tableau de retour
* @param {number} decy Valeur à retrancher au premier élément du tableau de retour
* @returns {Point}
*/
export function getTouchPositionToParent (event, parent, decx = 0, decy = 0) {
const { x, y } = parent.getBoundingClientRect()
return {
x: event.targetTouches[0].clientX - x - decx,
y: event.targetTouches[0].clientY - y - decy
}
}
export function divComp (z1, z2, zRes) {
const a = z1.x
const b = z1.y
const c = z2.x
const d = z2.y
if (Math.abs(d) <= Math.abs(c)) {
const r = d / c
const den = c + d * r
zRes.x = (a + b * r) / den
zRes.y = (b - a * r) / den
} else {
const r = c / d
const den = c * r + d
zRes.x = (a * r + b) / den
zRes.y = (b * r - a) / den
}
}
export const descriptionNature = [
'PointLibre', 'PointCons', 'PointLie',
'Barycentre', 'PointRep', 'PointLibreEnt',
'PointParAff', 'PointLiePoint', 'Macro',
'Dte', 'Vect', 'Seg', 'Ddte',
'Cerc', 'MarqueAng', 'Arc',
'Polygone', 'Surf', 'LieuPt',
'LieuPtDis', 'LigneBrisee',
'LieuObj', 'Image', 'GrapheSuiteR',
'GrapheSuiteComp', 'Comment', 'AffVal',
'MarqueSeg', 'ObjetDup', 'DemiPlan',
'PtIntPoly', 'Latex', 'PtIntCerc',
'EditeurFor', 'PtClone', 'DteClone', 'DdteClone',
'SegClone', 'CercClone'
]
// Ajout version 6.3.0
/**
* Fonction arrondissant une valeur à 3 chiffres après la virgule.
* Utilisée pour les lieux de points, polygones et lignes brisées.
* @param number
* @returns {number}
*/
export function round3dg (number) {
return number.toFixed(3)
}
/**
* Appelle preventDefault sur l'événement s'il le supporte (pour éviter de générer une erreur sinon)
* @param {Event} evt
*/
export function preventDefault (evt) {
if (typeof evt.cancelable !== 'boolean' || evt.cancelable) evt.preventDefault()
}
// Fonction ajoutée uniquement pour réparer le bug d'affichage de chrome des signes - et +
// quand on dézoome dans les navigateurs
/**
* Fonction renvoyant l'indice de l'accolade fermante acorrespondant à celle d'indice
* pdebut dans chaine.
* @param {string} chaine La chaine dans laquelle se fait la recherche. Le caractère
* d'indice pdebut doit être un {
* @param {number} pdebut L'indice du premier caractère de la recherche qui pointe sur une {
* @returns {number} : L'indice de la parenthèse fermante ou -1 s'il n'y en a pas.
*/
export function accoladeFermante (chaine, pdebut) {
let p
let ch
let somme
somme = 1
p = pdebut + 1
while (p < chaine.length) {
ch = chaine.charAt(p)
if (ch === '{') { somme++ } else {
if (ch === '}') { somme-- }
}
if (somme === 0) break
p++
}
if (somme === 0) return p
else return -1 // On renvoie -1 si pas trouvé
}
// Ajout version 6.7 pour les matrices
/**
* Fonction renvoyant la chaîne repésentant en LaTeX une matrice.
* Si tab est un nombre renvoie ce nombre entre parenthèses
* @param {number|MathJsChain} tab
* @param {number} nbdec Le nombre de décimales
* @returns {string|number}
*/
export function latexMat (tab, nbdec) {
if (typeof tab === 'number') return tab
const size = tab.size()
const n = size[0]
const p = size[1]
let res = '\\begin{matrix}'
for (let i = 0; i < n; i++) {
if (i !== 0) res += '\\\\'
for (let j = 0; j < p; j++) {
if (j !== 0) res += ' & '
const val = tab.get([i, j])
res += chaineNombre(val, nbdec)
}
}
return res + '\\end{matrix}'
}
// Ajout version 6.7 pour les matrices
/**
* Retourne la chaîne LaTeX d'une matrice où les valeurs sont remplacées
* par leur fraction continue aprrochée à 10^(-12) près
* Si tab est un nombre renvoie ce nombre entre parenthèses
* @param {number|MathJsChain} tab
* @returns {string}
*/
export function latexMatFrac (tab) {
if (typeof tab === 'number') {
if (tab === Math.floor(tab)) return String(tab)
if (Math.abs(tab) < 1e-12) return '0'
const tb = fracCont(tab)
// Pour le smatrices on utilise de \frac et pas des \frac
return '\\frac{' + tb[0] + '}{' + tb[1] + '}'
}
const size = tab.size()
const n = size[0]
const p = size[1]
let res = '\\begin{matrix}'
for (let i = 0; i < n; i++) {
if (i !== 0) res += '\\\\'
for (let j = 0; j < p; j++) {
if (j !== 0) res += ' & '
const val = tab.get([i, j])
if (val === Math.floor(val)) {
res += String(val)
} else {
if (Math.abs(val) < 1e-12) {
res += '0'
} else {
const tb = fracCont(val)
res += (tb[1] === 1)
? String(tb[0])
: '\\frac{' + tb[0] + '}{' + tb[1] + '}'
}
}
}
}
return res + '\\end{matrix}'
}
/**
* Fonction renvoyant un tableau de nombres correspondant à la matrice identité à n lignes et n colonnes
* @param n
* @returns {number[]}
*/
export function identity (n) {
const tab = []
for (let i = 0; i < n; i++) {
const lig = []
tab.push(lig)
for (let j = 0; j < n; j++) {
lig.push(i === j ? 1 : 0)
}
}
return tab
}
/**
* Fonction renvoyant un array de 2 valeurs [num, den] où num/den est la fraction continue apporchant x à 10^(-12) près.
* Si le nombre est en valeur absolue supérieur à 10^9 ou inférieur à 10^-9 ou si on dépasse 50
* boucles pour l'approximation on ne cherche pas à approximer et on renvoie [x, 1]
* @param {number} x Le nombre à approcher par une fraction continue
* @returns {number[]} un array [num, den]
*/
export function fracCont (x) {
function fracContPos (nb) {
let i = 0 // Compteur de boucles
const a0 = Math.floor(nb)
// if (a0 === nb) return [a0, 1]
if (Math.abs(nb - a0) < 1e-10) return [a0, 1]
let p0 = a0
let q0 = 1
const r1 = nb - a0
const a1 = Math.floor(1 / r1)
let p1 = a0 * a1 + 1
let q1 = a1
if (a1 === 1 / r1) return [p1, q1]
// const k = Math.pow(10, -prec)
// A priori cette fonction n'est utilisé que pour une approximation à 10^(-12) près
let p = p1
let q = q1
let r = r1
let a = a1
// while (Math.abs(nb - p / q) > k) {
while (Math.abs(nb - p / q) > 1e-12) {
i++
if (i > 50) return [x, 1] // Trop de boucles. On renvoie le nombre non approché par une fraction
r = 1 / r - a
a = Math.floor(1 / r)
p = a * p1 + p0
q = a * q1 + q0
if (a === 1 / r) return [p, q]
const oldp1 = p1
const oldq1 = q1
p1 = p
q1 = q
p0 = oldp1
q0 = oldq1
}
return [p, q]
}
if (Number.isInteger(x)) return [x, 1]
if (Math.abs(x) > 1000000000 || Math.abs(x) < 0.000000001) return [x, 1]
if (x >= 0) return fracContPos(x)
const res = fracContPos(-x)
return [-res[0], res[1]]
}
export function codeLatexFracCont (tab) {
if (tab[1] === 1) return String(tab[0])
return '\\dfrac{' + tab[0] + '}{' + tab[1] + '}'
}
/**
* Une fct à mettre dans les catch de promesse pour sortir l'erreur en console
* @type {function}
*/
export const consoleError = console.error.bind(console)
/**
* Remplace les \text{truc}acute{e} par du \text{trucé}
* @param {string} ch La chaîne à traiter
* @returns {string} La chaîne traitée
*/
export function traiteAccents (ch) {
const a = 'éèàùâêôî'
const b = ['acute', 'grave', 'grave', 'grave', 'hat', 'hat', 'hat', 'hat']
const c = 'eeauaeoi'
for (let i = 0; i < b.length; i++) {
const re = new RegExp('\\\\text{([^}]*?)}\\\\' + b[i] + '{' + c[i] + '}\\s*\\\\text{', 'g')
while (ch.search(re) !== -1) ch = ch.replace(re, '\\text{$1' + a[i])
const re2 = new RegExp('\\\\text{([^}]*?)}\\\\' + b[i] + '{' + c[i] + '}', 'g')
while (ch.search(re2) !== -1) ch = ch.replace(re2, '\\text{$1' + a[i] + '}')
}
// on vire les \text{} (textes vides)
ch = ch.replace(/\\text\{}/g, '')
// on merge les ≠ \text{}\text{}
const re3 = /\\text\{([^}]+)}\\text\{/g
while (re3.test(ch)) ch = ch.replace(re3, '\\text{$1')
return ch
}
/**
* @typedef DecompPrim
* @type Array
* @property {number[]} 0 la liste des facteurs premiers
* @property {number[]} 1 la liste des puissances de chaque facteur premier
*/
/**
* Retourne la décomposition en facteurs premiers
* @param {number} n
* @returns {DecompPrim}
*/
export function decompPrim (n) {
/**
* Fonction renvoyant true si le nombre k est un entier à 10^-9 près
* @inner
* @param k
* @returns {boolean}
*/
function entier (k) {
return zero(k - Math.round(k))
}
/**
* Fonction qui, si x est divisible par q, renvoie le plus grand exposant de q qui divise n et sinon renvoie 0
* @inner
* @param {number} x entier
* @param {number} q entier
* @returns {number}
*/
function divPar (x, q) {
if (entier(x / q)) {
let exp = 1
let k = q
while (entier(x / (q * k))) {
exp++
k = k * q
}
return exp
} else return 0
}
let nb = n
/** @type {number[]} */
const factprem = []
/** @type {number[]} */
const exposants = []
let exp = divPar(nb, 2)
if (exp > 0) {
factprem.push(2)
exposants.push(exp)
nb = nb / Math.pow(2, exp)
}
exp = divPar(nb, 3)
if (exp > 0) {
factprem.push(3)
exposants.push(exp)
nb = nb / Math.pow(3, exp)
}
let k = 5
let pas = 4
while (k * k <= nb) {
exp = divPar(nb, k)
if (exp > 0) {
factprem.push(k)
exposants.push(exp)
nb = nb / Math.pow(k, exp)
}
pas = 6 - pas
k = k + pas
}
if (nb !== 1) {
factprem.push(nb)
exposants.push(1)
}
return [factprem, exposants]
}
/**
* Affecte la propriété wheelListener au doc (pour pouvoir le retirer) et l'ajoute en listener wheel sur target
* (qui peut être un svg pour le cas du lecteur ou un rect svg pour le cas de l'appli)
* @param {CMathGraphDoc} doc
* @param {svg} svg Le svg sur lequel on agit
* @param {svg} target le svg element sur lequel on met le listener
*/
export function addZoomListener (doc, svg, target) {
const list = doc.listePr
const dimf = doc.dimf
const x = doc.dimf.x / 2
const y = doc.dimf.y / 2
let timeStart = Date.now()
doc.wheelListener = (event) => {
const time = Date.now()
event.preventDefault()
if ((time - timeStart) < 50) {
// event.stopPropagation()
return
}
const sens = event.deltaY < 0
const rap = sens ? constantes.rapportZoomPlus : constantes.rapportZoomMoins
timeStart = Date.now()
list.zoom(x, y, rap)
list.positionne(false, dimf)
list.update(svg, doc.couleurFond, true)
}
target.addEventListener('wheel', doc.wheelListener, { capture: false, passive: false })
}
/**
* Affiche l'erreur en console et notifie bugsnag s'il a été chargé
* @param {Error|string} error
*/
export function notify (error) {
if (typeof error === 'string') error = Error(error)
console.error(error)
if (window.bugsnagClient && typeof window.bugsnagClient.notify === 'function') {
window.bugsnagClient.notify(error)
}
}
/**
* Dans le cas où on utilise la virgule comme séparateur décimal pour les affichages, on doit
* pour retrouver la formule normale utilisant le point décimal appliquer cette fonction
* @param {MtgApp} app
* @param {string} ch
* @returns {string}
*/
export function replaceSepDecVirg (app, ch) {
if (app.decimalDot) return ch
else return ch.replaceAll(',', '.').replaceAll(';', ',')
}