YAxisStep as expression, selection box for X & Y axis labels, simplifications for expressions.
This commit is contained in:
parent
487daa426a
commit
47a4ac67a9
11 changed files with 232 additions and 111 deletions
|
@ -23,10 +23,21 @@
|
|||
|
||||
const parser = new ExprEval.Parser()
|
||||
|
||||
var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manualy
|
||||
"pi": Math.PI,
|
||||
"π": Math.PI,
|
||||
"inf": Infinity,
|
||||
"Infinity": Infinity,
|
||||
"∞": Infinity,
|
||||
"e": Math.E
|
||||
}
|
||||
|
||||
class Expression {
|
||||
constructor(expr) {
|
||||
this.expr = expr
|
||||
this.calc = parser.parse(expr).simplify()
|
||||
this.cached = this.isConstant()
|
||||
this.cachedValue = this.cached ? this.calc.evaluate(evalVariables) : null
|
||||
}
|
||||
|
||||
isConstant() {
|
||||
|
@ -34,19 +45,14 @@ class Expression {
|
|||
}
|
||||
|
||||
execute(x = 1) {
|
||||
return this.calc.evaluate({
|
||||
"x": x,
|
||||
"pi": Math.PI,
|
||||
"π": Math.PI,
|
||||
"inf": Infinity,
|
||||
"Infinity": Infinity,
|
||||
"∞": Infinity,
|
||||
"e": Math.E
|
||||
})
|
||||
if(this.cached) return this.cachedValue
|
||||
return this.calc.evaluate(Object.assign({'x': x}, evalVariables))
|
||||
}
|
||||
|
||||
simplify(x = 1) {
|
||||
return Utils.makeExpressionReadable(this.calc.substitute('x', x).simplify().toString())
|
||||
var expr = this.calc.substitute('x', x).simplify()
|
||||
if(expr.evaluate(evalVariables) == 0) return '0'
|
||||
return Utils.makeExpressionReadable(expr.toString())
|
||||
}
|
||||
|
||||
toEditableString() {
|
||||
|
|
|
@ -200,7 +200,7 @@ class Function extends ExecutableObject {
|
|||
|
||||
getReadableString() {
|
||||
if(this.displayMode == 'application') {
|
||||
return `${this.name}: ${this.inDomain} ⸺˃ ${this.outDomain}\n ${' '.repeat(this.name.length)}x ⸺˃ ${this.expression.toString()}`
|
||||
return `${this.name}: ${this.inDomain} ⸺> ${this.outDomain}\n ${' '.repeat(this.name.length)}x ⸺> ${this.expression.toString()}`
|
||||
} else {
|
||||
return `${this.name}(x) = ${this.expression.toString()}`
|
||||
}
|
||||
|
|
|
@ -119,27 +119,105 @@ function textsub(text) {
|
|||
return ret
|
||||
}
|
||||
|
||||
function simplifyExpression(str) {
|
||||
var replacements = [
|
||||
// Operations not done by parser.
|
||||
[ // Removing parenthesis when content is only added from both sides.
|
||||
/(^.?|[+-] |\()\(([^)(]+)\)(.?$| [+-]|\))/g,
|
||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[ // Removing parenthesis when content is only multiplied.
|
||||
/(^.?|[*\/] |\()\(([^)(+-]+)\)(.?$| [*\/]|\))/g,
|
||||
function(match, b4, middle, after) {return `${b4}${middle}${after}`}
|
||||
],
|
||||
[// Simplification additions/substractions.
|
||||
/(^.?|[^*\/] |\()([-.\d]+) (\+|\-) (\([^)(]+\)|[^)(]+) (\+|\-) ([-.\d]+)(.?$| [^*\/]|\))/g,
|
||||
function(match, b4, n1, op1, middle, op2, n2, after) {
|
||||
var total
|
||||
if(op2 == '+') {
|
||||
total = parseFloat(n1) + parseFloat(n2)
|
||||
} else {
|
||||
total = parseFloat(n1) - parseFloat(n2)
|
||||
}
|
||||
return `${b4}${total} ${op1} ${middle}${after}`
|
||||
}
|
||||
],
|
||||
[// Simplification multiplications/divisions.
|
||||
/([-.\d]+) (\*|\/) (\([^)(]+\)|[^)(+-]+) (\*|\/) ([-.\d]+)/g,
|
||||
function(match, n1, op1, middle, op2, n2) {
|
||||
if(parseInt(n1) == n1 && parseInt(n2) == n2 && op2 == '/' &&
|
||||
(parseInt(n1) / parseInt(n2)) % 1 != 0) {
|
||||
// Non int result for int division.
|
||||
return `(${n1} / ${n2}) ${op1} ${middle}`
|
||||
} else {
|
||||
if(op2 == '*') {
|
||||
return `${parseFloat(n1) * parseFloat(n2)} ${op1} ${middle}`
|
||||
} else {
|
||||
return `${parseFloat(n1) / parseFloat(n2)} ${op1} ${middle}`
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[// Starting & ending parenthesis if not needed.
|
||||
/^\((.*)\)$/g,
|
||||
function(match, middle) {
|
||||
var str = middle
|
||||
// Replace all groups
|
||||
while(/\([^)(]+\)/g.test(str))
|
||||
str = str.replace(/\([^)(]+\)/g, '')
|
||||
// There shouldn't be any more parenthesis
|
||||
// If there is, that means the 2 parenthesis are needed.
|
||||
if(!str.includes(')') && !str.includes('(')) {
|
||||
return middle
|
||||
} else {
|
||||
return `(${middle})`
|
||||
}
|
||||
|
||||
}
|
||||
],
|
||||
// Simple simplifications
|
||||
[/(\s|^|\()0 \* (\([^)(]+\))/g, '$10'],
|
||||
[/(\s|^|\()0 \* ([^)(+-]+)/g, '$10'],
|
||||
[/(\([^)(]\)) \* 0(\s|$|\))/g, '0$2'],
|
||||
[/([^)(+-]) \* 0(\s|$|\))/g, '0$2'],
|
||||
[/(\s|^|\()1 (\*|\/) /g, '$1'],
|
||||
[/(\s|^|\()0 (\+|\-) /g, '$1'],
|
||||
[/ (\*|\/) 1(\s|$|\))/g, '$2'],
|
||||
[/ (\+|\-) 0(\s|$|\))/g, '$2'],
|
||||
[/(^| |\() /g, '$1'],
|
||||
[/ ($|\))/g, '$1'],
|
||||
]
|
||||
|
||||
console.log(str)
|
||||
// Replacements
|
||||
replacements.forEach(function(replacement){
|
||||
while(replacement[0].test(str))
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
function makeExpressionReadable(str) {
|
||||
var replacements = [
|
||||
// variables
|
||||
[/pi/g, 'π'],
|
||||
[/Infinity/g, '∞'],
|
||||
[/inf/g, '∞'],
|
||||
// Other
|
||||
[/ \* /g, '×'],
|
||||
[/ \^ /g, '^'],
|
||||
[/\^\(([^\^]+)\)/g, function(match, p1) { return textsup(p1) }],
|
||||
[/\^([^ ]+)/g, function(match, p1) { return textsup(p1) }],
|
||||
[/(\d|\))×/g, '$1'],
|
||||
[/×(\d|\()/g, '$1'],
|
||||
[/\(([^)(+.-]+)\)/g, "$1"],
|
||||
[/\(([^)(+.-]+)\)/g, "$1"],
|
||||
[/\(([^)(+.-]+)\)/g, "$1"],
|
||||
[/\(([^)(+.-]+)\)/g, "$1"],
|
||||
[/\(([^)(+.-]+)\)/g, "$1"],
|
||||
// Doing it 4 times to be recursive until better implementation
|
||||
[/\(([^)(+.\/-]+)\)/g, "$1"],
|
||||
]
|
||||
|
||||
str = simplifyExpression(str)
|
||||
// Replacements
|
||||
replacements.forEach(function(replacement){
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
while(replacement[0].test(str))
|
||||
str = str.replace(replacement[0], replacement[1])
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
@ -184,7 +262,7 @@ function parseName(str, removeUnallowed = true) {
|
|||
[/_\(([^\^]+)\)/g, function(match, p1) { return textsub(p1) }],
|
||||
[/_([^ ]+)/g, function(match, p1) { return textsub(p1) }],
|
||||
// Removing
|
||||
[/[xπℝℕ\\∪∩\]\[ ()^/÷*×+=\d-]/g , function(match){console.log('removing', match); return ''}],
|
||||
[/[xπℝℕ\\∪∩\]\[ ()^/÷*×+=\d-]/g , ''],
|
||||
]
|
||||
if(!removeUnallowed) replacements.pop()
|
||||
// Replacements
|
||||
|
@ -194,6 +272,7 @@ function parseName(str, removeUnallowed = true) {
|
|||
return str
|
||||
}
|
||||
|
||||
|
||||
String.prototype.toLatinUppercase = function() {
|
||||
return this.replace(/[a-z]/g, function(match){return match.toUpperCase()})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue