Началось все с того, что около трех или четырех лет назад я написал на 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.