Java
Mathematics
9 October 2014

Рисуем графики онлайн

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

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.

+5
14.1k 67
Comments 14