Началось все с того, что около трех или четырех лет назад я написал на Java собственный парсер и калькулятор математических выражений — jExpressions.
И вот, относительно недавно, в свете осваивания технологии Java EE, возникла идея сделать на основе этого парсера онлайн рисовалку графиков.
Парсер на тот момент обладал довольно экзотическим синтаксисом для вызова функций (напр. exp#3 вместо exp(3); beta#1:2 вместо beta(1,2)).
Также время от времени вылетали баги.
После нескольких часов допиливания и обезглючивания появилась на свет версия jExpressions 1.0.
После этого можно было приступить к делу.
Рисовалка графиков устроена так:
1. Cервлет вычисляет значения функции для N-ного количества точек (250 для простых и 1000 для параметрических) и выдает результат в формате json.
2. Веб-страница получает json от сервлета и рисует график с помощью плагина Flot.
Код функции processRequest() сервлета:
js-код для построения графика:
Результат можно увидеть здесь (простые функции) и здесь (параметрические).
Примечание: работает довольно медленно, так как сервер крутится на Raspberry Pi.
И вот, относительно недавно, в свете осваивания технологии Java EE, возникла идея сделать на основе этого парсера онлайн рисовалку графиков.
Парсер на тот момент обладал довольно экзотическим синтаксисом для вызова функций (напр. exp#3 вместо exp(3); beta#1:2 вместо beta(1,2)).
Также время от времени вылетали баги.
После нескольких часов допиливания и обезглючивания появилась на свет версия jExpressions 1.0.
После этого можно было приступить к делу.
Рисовалка графиков устроена так:
1. Cервлет вычисляет значения функции для N-ного количества точек (250 для простых и 1000 для параметрических) и выдает результат в формате json.
2. Веб-страница получает json от сервлета и рисует график с помощью плагина Flot.
Код функции processRequest() сервлета:
private void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
String result = "{ \"result\" : ";
String func = request.getParameter("func");
String var = request.getParameter("variable");
String begin = request.getParameter("begin");
String end = request.getParameter("end");
if (func==null||var==null||begin==null||end==null) {
result += "\"error\", \"err\" : \"param\"}";
out.println(result);
return;
}
// upd:
//Double x0 = Double.parseDouble(begin);
//Double x1 = Double.parseDouble(end);
Double x0;
Double x1;
try {
x0 = Double.parseDouble(begin);
x1 = Double.parseDouble(end);
} catch (Exception e) {
result += "\"error\", \"err\" : \"param\"}";
out.println(result);
return;
}
int num = 0;
try {
Parser parser = new Parser();
num = parser.setAlias(var); // сообщаем парсеру имя переменной
long time0 = System.nanoTime();
Expr expr = parser.parseExpr(parser.prepareExpr(func)); // на основе выражения строится двоичное дерево
String xArr = "[";
String yArr = "[";
long time1 = System.nanoTime();
for (int i=0; i<=points; i++) {
if (i>0) {
xArr += ", ";
yArr += ", ";
}
Double x = x0 + ((x1-x0)/points)*i;
parser.setVar(num, x); // подставляем значение переменной
Double y = expr.evaluate(); // вычисляем выражение
xArr += x.toString();
yArr += y.toString();
}
long time2 = System.nanoTime();
Integer parse_time = (int)((time1-time0)/1e6);
Integer calc_time = (int)((time2-time1)/1e6);
Integer total_time = (int)((time2-time0)/1e6);
System.out.println("Parse time: "+parse_time.toString()
+"; Calc time: "+calc_time.toString());
xArr += "]";
yArr += "]";
result += "\"ok\", \"time\" : \""+total_time.toString()
+"\", \"x\" : \""+xArr+"\", \"y\" : \""+yArr+"\"}";
} catch (BadVarException e) {
result += "\"error\", \"err\" : \"badvar\"}";
} catch (Exception e) {
result += "\"error\", \"err\" : \"wrong\"}";
} finally {
out.print(result);
}
}
js-код для построения графика:
window.getResult = function(func,variable,begin,end){
$('#draw').prop('disabled',true);
$('#info').text('Идет вычисление...');
$.post('/functions/calc',
{func: func, variable: variable, begin: begin, end: end},
function(data){
window.result = $.parseJSON(data);
if (result.result == 'error') {
if (result.err == 'wrong') $('#info').text('Ошибка в выражении');
if (result.err == 'badvar') $('#info').text('Имя переменной зарезервировано');
$('#draw').prop('disabled',false);
return;
}
window.xArr = $.parseJSON(result.x);
result.y = result.y
.split('-Infinity').join('null')
.split('Infinity').join('null')
.split('NaN').join('null');
window.yArr = $.parseJSON(result.y);
window.data = [];
for (i=0; i<xArr.length; i++) {
y = window.yArr[i];
window.data[i] = [window.xArr[i],window.yArr[i]];
}
window.drawFunc();
var millis = result.time%1000;
var sec = (result.time-millis)/1000;
$('#info').text('Вычислено за '+sec+'.'+millis+' секунд');
$('#draw').prop('disabled',false);
});
};
window.drawFunc = function() {
var data_arr = [];
data_arr[0] = {data:window.data, color:'#f00'};
$.plot($('#graph'),data_arr);
};
Результат можно увидеть здесь (простые функции) и здесь (параметрические).
Примечание: работает довольно медленно, так как сервер крутится на Raspberry Pi.