Skip to content

Commit

Permalink
Merge pull request #38 from quintindunn/dva-t-graphing-mathjs-port
Browse files Browse the repository at this point in the history
Remove expr-eval and substitute with math.js
  • Loading branch information
quintindunn authored Oct 21, 2023
2 parents 69df54f + 3e4e77c commit 62432d0
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 64 deletions.
12 changes: 5 additions & 7 deletions static/graphs/js/dva_graphs/condition_processing.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Parser } from "/static/node_modules/expr-eval/dist/index.mjs";

// What operators to check for in the piecewise function (pwf)
const OPERATORS = ['<', '<=', '>', '>='];

// Generate the regex pattern given the operators.
const PATTERN = new RegExp('(' + OPERATORS.map(operator => operator.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')).join('|') + ')');

// Splits the condition, so it can be evaluated piece by piece (10<x<15 -> ["10<x", "x<15"]
// Splits the condition, so it can be evaluated piece by piece (10<t<15 -> ["10<t", "t<15"]
function splitExpr(expr) {
// Split the expression using the regex pattern
const result = expr.split(PATTERN);
Expand All @@ -17,12 +15,12 @@ function splitExpr(expr) {
comparisons.push(result.slice(i, i + 3));
}

// append/prepend `x` to the conditions, so it can be evaluated one by one. prepend if first char is an operator, append if first character is a number.
// append/prepend `t` to the conditions, so it can be evaluated one by one. prepend if first char is an operator, append if first character is a number.
return comparisons.map(comparison => {
if (OPERATORS.includes(comparison[1])) {
return comparison.join('');
} else if (OPERATORS.includes(comparison[0])) {
return 'x' + comparison.join('');
return 't' + comparison.join('');
}
});
}
Expand All @@ -33,8 +31,8 @@ export function evalExpr(expr, x) {

// Loop through the expressions and evaluate each one, if any returns false, return false, otherwise return true.
for (const subExpr of expressions) {
const evalExpr = subExpr.replace('x', x.toString());
if (Parser.evaluate(evalExpr, { x: x }) === false) {
const evalExpr = subExpr.replace('t', x.toString());
if (math.evaluate([`t=` + x, evalExpr])[1] === false) {
return false; // Condition failed.
}
}
Expand Down
17 changes: 9 additions & 8 deletions static/graphs/js/dva_graphs/dva_graphs.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
import { MathFunction, PiecewiseFunction, preParseExpression } from "/static/graphs/js/dva_graphs/function.js";
import { MathFunction, PiecewiseFunction } from "/static/graphs/js/dva_graphs/function.js";
import { evalExpr } from "./condition_processing.js";

import { Parser } from "/static/node_modules/expr-eval/dist/index.mjs";

// Declare the chart dimensions and margins.
const equation_error_div = document.getElementById("error-div")

Expand Down Expand Up @@ -102,7 +100,7 @@ function checkInput(expr, is_condition=false) {
if (is_condition) {
// Check if it is valid
try {
evalExpr(expr, 0); // 0 As placeholder, if this throws an error it is incorrect.
math.evaluate(['t=0', expr]);
} catch (err) {
return CODES.ERROR;
}
Expand All @@ -116,7 +114,11 @@ function checkInput(expr, is_condition=false) {
let parsed;

try {
parsed = Parser.parse(expr).toJSFunction("x"); // Return a function that takes `x` to evaluate the expr.
// Test expression to make sure it is valid.
math.evaluate(["t=" + 0, expr])

// Return a function that takes `x` to evaluate the expr.
parsed = (x) => {return math.evaluate(["t=" + x, expr])[1]};
}
catch(err) {
return parsed ?? CODES.ERROR; // expression is invalid
Expand Down Expand Up @@ -166,11 +168,10 @@ function graph_function() {

// Redundancy check.
if (inp_name === "equation" || inp_name === "condition") {
// preparse the expression to allow implicit multiplication i.e. 5x -> 5*x.
let preparsed = preParseExpression(inp.value, ['x']);
const expr = inp.value.replaceAll("x", "t") // Use `t` instead of `x` as mathjs throws an error with 0x

// Validate the input, if it's true returns a function where you pass in the `x` value.
let jsFunc_eq = checkInput(preparsed, inp_name === "condition");
let jsFunc_eq = checkInput(expr, inp_name === "condition");

// Blank, skip the expr.
if (jsFunc_eq === 0) continue;
Expand Down
40 changes: 1 addition & 39 deletions static/graphs/js/dva_graphs/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,42 +79,4 @@ export class PiecewiseFunction {

return pointsArr;
}
}


export function preParseExpression(expressionString, variables) {
// CREDIT: https://github.com/silentmatt/expr-eval/issues/1#issuecomment-42589068
variables = variables.join('');

// Build an object with replacement rules. (The order matters!)
let re = {};
// Turns 'x^xy' into 'x^(x*y)'.
re.parenthesizeVariables = {
expr : new RegExp('([0-9' + variables + ']+)([' + variables + ']+)'),
repl : '($1*$2)',
};
// Turns '2(3+y)' into '2*(3+y)'.
re.parenthesisCoefficients = {
expr : /(\d+)([(])/i,
repl : '$1*$2'
};
// Turns '(x^xy)(x-y)' into '(x^xy)*(x-y)'.
re.parenthesisMultiplication = {
expr : /([)])([(])/,
repl : '$1*$2',
};
// Turns '2sin' into '2*sin'.
re.functionCoefficients = {
expr : /(\d+)([a-z]+)/i,
repl : '$1*$2',
};

// Apply the replacement rules.
for (let i in re) {
while (expressionString.replace(re[i].expr, re[i].repl) !== expressionString) {
expressionString = expressionString.replace(re[i].expr, re[i].repl);
}
}

return expressionString;
}
}
172 changes: 163 additions & 9 deletions static/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
"homepage": "https://github.com/quintindunn/APhysicsWeb#readme",
"dependencies": {
"d3": "^7.8.5",
"expr-eval": "^2.0.2"
"mathjs": "^11.11.2"
}
}
Loading

0 comments on commit 62432d0

Please sign in to comment.