LogarithmPlotter/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/latex.js

261 lines
9.6 KiB
JavaScript
Raw Normal View History

/**
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
* Copyright (C) 2022 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/>.
*/
2022-03-05 16:35:58 +00:00
.pragma library
2022-03-05 16:35:58 +00:00
.import "../expr-eval.js" as ExprEval
2022-03-07 19:28:50 +00:00
/**
* true if latex has been enabled by the user, false otherwise.
*/
var enabled = false
2022-03-05 15:43:22 +00:00
/**
* Puts element within parenthesis.
*
* @param {string} elem - element to put within parenthesis.
* @returns {string}
*/
function par(elem) {
return '(' + elem + ')'
}
2022-03-05 15:43:22 +00:00
/**
* 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.
2022-03-05 15:43:22 +00:00
*
* @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":
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}';
break;
case "integral":
return '\\int\\limits_{' + args[0] + '}^{' + args[1] + '}' + args[2].substr(1, args[2].length-2) + ' d' + args[3].substr(1, args[3].length-2);
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;
}
}
2022-03-05 15:43:22 +00:00
/**
* Creates a latex variable from a variable.
*
* @param {string} vari - variable to convert
* @returns {string}
*/
function variable(vari) {
2022-03-05 20:00:29 +00:00
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}",
2022-03-05 20:00:29 +00:00
"{}_{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"]
2022-03-05 20:00:29 +00:00
for(let i = 0; i < unicodechars.length; i++) {
//console.log(vari, unicodechars[i], equivalchars[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.
2022-03-05 15:43:22 +00:00
*
* @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 (typeof item.value === 'number' && item.value < 0) {
nstack.push(par(item.value));
} else if (Array.isArray(item.value)) {
2022-03-05 16:35:58 +00:00
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
} else {
2022-03-05 16:35:58 +00:00
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();
2022-03-05 16:35:58 +00:00
// 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(';') ]
}
2022-03-05 16:35:58 +00:00
return String(nstack[0]);
}