LogarithmPlotter/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js
Ad5001 fde2526c54
Some checks failed
continuous-integration/drone/push Build is failing
Updating copyrights
2024-01-11 00:11:09 +01:00

283 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2021-2024 Ad5001
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
.pragma library
.import "../expr-eval.js" as ExprEval
/**
* true if latex has been enabled by the user, false otherwise.
*/
var enabled = false
/**
* LaTeX python backend QObject.
*/
var Renderer = null
/**
* Default window color used to render LaTeX formulas.
*/
var defaultColor = "black"
/**
* Puts element within parenthesis.
*
* @param {string} elem - element to put within parenthesis.
* @returns {string}
*/
function par(elem) {
return '(' + elem + ')'
}
/**
* Checks if the element contains at least one of the elements of
* the string array contents, but not at the first position of the string,
* and returns the parenthesis version if so.
*
* @param {string} elem - element to put within parenthesis.
* @param {Array} contents - Array of elements to put within parenthesis.
* @returns {string}
*/
function parif(elem, contents) {
elem = elem.toString()
if(elem[0] != "(" && elem[elem.length-1] != ")" && contents.some(x => elem.indexOf(x) > 0))
return par(elem)
if(elem[0] == "(" && elem[elem.length-1] == ")")
return elem.substr(1, elem.length-2)
return elem
}
/**
* Creates a latex expression for a function.
*
* @param {string} f - Function to convert
* @param {Array} args - Arguments of the function
* @returns {string}
*/
function functionToLatex(f, args) {
switch(f) {
case "derivative":
if(args.length == 3)
return '\\frac{d' + args[0].substr(1, args[0].length-2).replace(new RegExp(args[1].substr(1, args[1].length-2), 'g'), 'x') + '}{dx}';
else
return '\\frac{d' + args[0] + '}{dx}(x)';
break;
case "integral":
if(args.length == 4)
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2);
else
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2] + '(t) dt';
break;
case "sqrt":
return '\\sqrt\\left(' + args.join(', ') + '\\right)';
break;
case "abs":
return '\\left|' + args.join(', ') + '\\right|';
break;
case "floor":
return '\\left\\lfloor' + args.join(', ') + '\\right\\rfloor';
break;
case "ceil":
return '\\left\\lceil' + args.join(', ') + '\\right\\rceil';
break;
default:
return '\\mathrm{' + f + '}\\left(' + args.join(', ') + '\\right)';
break;
}
}
/**
* Creates a latex variable from a variable.
*
* @param {string} vari - variable text to convert
* @param {bool} wrapIn$ - checks whether the escaped chars should be escaped
* @returns {string}
*/
function variable(vari, wrapIn$ = false) {
let unicodechars = ["α","β","γ","δ","ε","ζ","η",
"π","θ","κ","λ","μ","ξ","ρ",
"ς","σ","τ","φ","χ","ψ","ω",
"Γ","Δ","Θ","Λ","Ξ","Π","Σ",
"Φ","Ψ","Ω","ₐ","ₑ","ₒ","ₓ",
"ₕ","ₖ","ₗ","ₘ","ₙ","ₚ","ₛ",
"ₜ","¹","²","³","⁴","⁵","⁶",
"⁷","⁸","⁹","⁰","₁","₂","₃",
"₄","₅","₆","₇","₈","₉","₀",
"pi", "∞"]
let equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta","\\eta",
"\\pi","\\theta","\\kappa","\\lambda","\\mu","\\xi","\\rho",
"\\sigma","\\sigma","\\tau","\\phi","\\chi","\\psi","\\omega",
"\\Gamma","\\Delta","\\Theta","\\Lambda","\\Xi","\\Pi","\\Sigma",
"\\Phy","\\Psi","\\Omega","{}_{a}","{}_{e}","{}_{o}","{}_{x}",
"{}_{h}","{}_{k}","{}_{l}","{}_{m}","{}_{n}","{}_{p}","{}_{s}",
"{}_{t}","{}^{1}","{}^{2}","{}^{3}","{}^{4}","{}^{5}","{}^{6}",
"{}^{7}","{}^{8}","{}^{9}","{}^{0}","{}_{1}","{}_{2}","{}_{3}",
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}",
"\\pi", "\\infty"]
if(wrapIn$)
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], 'g'), '$'+equivalchars[i]+'$')
}
else
for(let i = 0; i < unicodechars.length; i++) {
if(vari.includes(unicodechars[i]))
vari = vari.replace(new RegExp(unicodechars[i], 'g'), equivalchars[i])
}
return vari;
}
/**
* Converts expr-eval tokens to a latex string.
*
* @param {Array} tokens - expr-eval tokens list
* @returns {string}
*/
function expression(tokens) {
var nstack = [];
var n1, n2, n3;
var f, args, argCount;
for (var i = 0; i < tokens.length; i++) {
var item = tokens[i];
var type = item.type;
switch(type) {
case ExprEval.INUMBER:
if(item.value == Infinity) {
nstack.push("\\infty")
} else if(typeof item.value === 'number' && item.value < 0) {
nstack.push(par(item.value));
} else if(Array.isArray(item.value)) {
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
} else {
nstack.push(ExprEval.escapeValue(item.value));
}
break;
case ExprEval.IOP2:
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
switch(f) {
case '-':
case '+':
nstack.push(n1 + f + n2);
break;
case '||':
case 'or':
case '&&':
case 'and':
case '==':
case '!=':
nstack.push(par(n1) + f + par(n2));
break;
case '*':
if(n2 == "\\pi" || n2 == "e" || n2 == "x" || n2 == "n")
nstack.push(parif(n1,['+','-']) + n2)
else
nstack.push(parif(n1,['+','-']) + " \\times " + parif(n2,['+','-']));
break;
case '/':
nstack.push("\\frac{" + n1 + "}{" + n2 + "}");
break;
case '^':
nstack.push(parif(n1,['+','-','*','/','!']) + "^{" + n2 + "}");
break;
case '%':
nstack.push(parif(n1,['+','-','*','/','!','^']) + " \\mathrm{mod} " + parif(n2,['+','-','*','/','!','^']));
break;
case '[':
nstack.push(n1 + '[' + n2 + ']');
break;
default:
throw new EvalError("Unknown operator " + ope + ".");
}
break;
case ExprEval.IOP3: // Thirdiary operator
n3 = nstack.pop();
n2 = nstack.pop();
n1 = nstack.pop();
f = item.value;
if (f === '?') {
nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')');
} else {
throw new EvalError('Unknown operator ' + ope + '.');
}
break;
case ExprEval.IVAR:
case ExprEval.IVARNAME:
nstack.push(variable(item.value.toString()));
break;
case ExprEval.IOP1: // Unary operator
n1 = nstack.pop();
f = item.value;
switch(f) {
case '-':
case '+':
nstack.push(par(f + n1));
break;
case '!':
nstack.push(parif(n1,['+','-','*','/','^']) + '!');
break;
default:
nstack.push(f + parif(n1,['+','-','*','/','^']));
break;
}
break;
case ExprEval.IFUNCALL:
argCount = item.value;
args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
f = nstack.pop();
// Handling various functions
nstack.push(functionToLatex(f, args))
break;
case ExprEval.IFUNDEF:
nstack.push(par(n1 + '(' + args.join(', ') + ') = ' + n2));
break;
case ExprEval.IMEMBER:
n1 = nstack.pop();
nstack.push(n1 + '.' + item.value);
break;
case ExprEval.IARRAY:
argCount = item.value;
args = [];
while (argCount-- > 0) {
args.unshift(nstack.pop());
}
nstack.push('[' + args.join(', ') + ']');
break;
case ExprEval.IEXPR:
nstack.push('(' + expression(item.value) + ')');
break;
case ExprEval.IENDSTATEMENT:
break;
default:
throw new EvalError('invalid Expression');
break;
}
}
if (nstack.length > 1) {
nstack = [ nstack.join(';') ]
}
return String(nstack[0]);
}