[SCM] Packaging for mathgl branch, experimental, updated. debian/2.1.2-1-3-gf31a279

Dimitrios Eftaxiopoulos eftaxi12 at otenet.gr
Thu Apr 25 14:33:58 UTC 2013


The following commit has been merged in the experimental branch:
commit 59b4eec7fd5c7b75915287ffd7a59a931bd16cd8
Author: Dimitrios Eftaxiopoulos <eftaxi12 at otenet.gr>
Date:   Sun Apr 21 18:44:16 2013 +0300

    Imported Upstream version 2.1.2+svn722

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9469288..583f290 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,6 +11,48 @@ set(CMAKE_VERBOSE_MAKEFILE ON)
 set(MathGL_VERSION_MAJOR 2)
 set(MathGL_VERSION_MINOR 1.2)
 
+
+MACRO(MGL_DEPENDENT_OPTION option doc default depends1 force1 depends2 force2)
+  IF(${option}_ISSET MATCHES "^${option}_ISSET$")
+    SET(${option}_AVAILABLE 1)
+    IF(${force1})
+    FOREACH(d ${depends1})
+      STRING(REGEX REPLACE " +" ";" CMAKE_DEPENDENT_OPTION_DEP "${d}")
+      IF(${CMAKE_DEPENDENT_OPTION_DEP})
+      ELSE(${CMAKE_DEPENDENT_OPTION_DEP})
+          SET(${option}_AVAILABLE 0)
+          SET(depends1_AVAILABLE 1)
+      ENDIF(${CMAKE_DEPENDENT_OPTION_DEP})
+    ENDFOREACH(d)
+    ENDIF(${force1})
+    IF(${force2})
+    FOREACH(d ${depends2})
+      STRING(REGEX REPLACE " +" ";" CMAKE_DEPENDENT_OPTION_DEP "${d}")
+      IF(${CMAKE_DEPENDENT_OPTION_DEP})
+      ELSE(${CMAKE_DEPENDENT_OPTION_DEP})
+          SET(${option}_AVAILABLE 0)
+          SET(depends2_AVAILABLE 1)
+      ENDIF(${CMAKE_DEPENDENT_OPTION_DEP})
+    ENDFOREACH(d)
+    ENDIF(${force2})
+    IF(${option}_AVAILABLE)
+      OPTION(${option} "${doc}" "${default}")
+      SET(${option} "${${option}}" CACHE BOOL "${doc}" FORCE)
+    ELSE(${option}_AVAILABLE)
+      IF(${option} MATCHES "^${option}$")
+      ELSE(${option} MATCHES "^${option}$")
+        SET(${option} "${${option}}" CACHE INTERNAL "${doc}")
+      ENDIF(${option} MATCHES "^${option}$")
+        IF(depends1_AVAILABLE)
+          SET(${option} OFF)
+        ELSEIF(depends2_AVAILABLE)
+          SET(${option} ON)
+        ENDIF(depends1_AVAILABLE)
+    ENDIF(${option}_AVAILABLE)
+  ELSE(${option}_ISSET MATCHES "^${option}_ISSET$")
+    SET(${option} "${${option}_ISSET}")
+  ENDIF(${option}_ISSET MATCHES "^${option}_ISSET$")
+ENDMACRO(MGL_DEPENDENT_OPTION)
 include(CMakeDependentOption)
 
 set(MGL_LIB_INSTALL_DIR "lib" CACHE STRING "Set library install directory")
@@ -18,29 +60,30 @@ set(MGL_LIB_INSTALL_DIR "lib" CACHE STRING "Set library install directory")
 option(enable-double "Enable double precision in MathGL library" ON)
 option(enable-simple "Slightly increase drawing speed but disable mglDataA class")
 option(enable-mpi "Enable mpi")
+option(enable-doc "Enable documentation building")
 option(enable-all "Enable all core features")
 option(enable-all-widgets "Enable all Widgets")
 option(enable-all-swig "Enable all SWIG based interfaces")
-option(enable-lgpl "Enable only LGPL part of MathGL" OFF)
+option(enable-pthread "Enable POSIX threads support" ON)
+option(enable-lgpl "Enable only LGPL part of MathGL")
+option(enable-mgl2 "Use names 'libmgl2-*' instead of 'libmgl-*'")
 #option(enable-ltdl "Enable loading modules support")
-option(enable-pthread "Enable POSIX threads support")
-option(enable-gsl "Enable gsl support")
-option(enable-jpeg "Enable jpeg support")
-option(enable-png "Enable png support" ON)
-option(enable-zlib "Enable zlib support" ON)
-option(enable-pdf "Enable pdf support")
-option(enable-gif "Enable gif support")
-option(enable-hdf4 "Enable hdf4 support")
-option(enable-hdf5 "Enable hdf5 support")
-option(enable-opengl "Enable OpenGL support" ON)
-option(enable-glut "Enable glut support")
-option(enable-fltk "Enable fltk widget")
-option(enable-wx "Enable wxWidget widget")
-option(enable-qt "Enable Qt4 widget")
-option(enable-python "Enable python interface")
-option(enable-octave "Enable octave interface")
-option(enable-octave-install "Octave interface will install for all users" ON)
-option(enable-doc "Enable documentation building")
+CMAKE_DEPENDENT_OPTION(enable-zlib "Enable zlib support" ON "NOT enable-all" ON)
+CMAKE_DEPENDENT_OPTION(enable-png "Enable png support" ON "NOT enable-all" ON)
+CMAKE_DEPENDENT_OPTION(enable-jpeg "Enable jpeg support" OFF "NOT enable-all" ON)
+MGL_DEPENDENT_OPTION(enable-gsl "Enable gsl support" OFF "NOT enable-lgpl" ON "NOT enable-all" ON)
+MGL_DEPENDENT_OPTION(enable-hdf4 "Enable hdf4 support" OFF "NOT enable-lgpl" ON "NOT enable-all" ON)
+MGL_DEPENDENT_OPTION(enable-hdf5 "Enable hdf5 support" OFF "NOT enable-lgpl" ON "NOT enable-all" ON)
+CMAKE_DEPENDENT_OPTION(enable-pdf "Enable pdf support" OFF "NOT enable-all" ON)
+CMAKE_DEPENDENT_OPTION(enable-gif "Enable gif support" OFF "NOT enable-all" ON)
+CMAKE_DEPENDENT_OPTION(enable-opengl "Enable OpenGL support" ON "NOT enable-mpi" OFF)
+MGL_DEPENDENT_OPTION(enable-glut "Enable glut support" OFF "NOT enable-lgpl;NOT enable-mpi" ON "NOT enable-all-widgets" ON)
+MGL_DEPENDENT_OPTION(enable-fltk "Enable fltk widget" OFF "NOT enable-lgpl;NOT enable-mpi" ON "NOT enable-all-widgets" ON)
+CMAKE_DEPENDENT_OPTION(enable-wx "Enable wxWidget widget" OFF "NOT enable-lgpl;NOT enable-mpi" OFF)
+MGL_DEPENDENT_OPTION(enable-qt "Enable Qt4 widget" OFF "NOT enable-lgpl;NOT enable-mpi" ON "NOT enable-all-widgets" ON)
+MGL_DEPENDENT_OPTION(enable-python "Enable python interface" OFF "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
+MGL_DEPENDENT_OPTION(enable-octave "Enable octave interface" OFF "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
+MGL_DEPENDENT_OPTION(enable-octave-install "Octave interface will install for all users" ON "NOT enable-lgpl" ON "NOT enable-all-swig" ON)
 
 include_directories( ${MathGL_SOURCE_DIR}/include ${MathGL_BINARY_DIR}/include)
 set(MGL_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/include/mgl2")
@@ -83,7 +126,17 @@ else(enable-mpi)
 	set(MGL_HAVE_MPI 0)
 endif(enable-mpi)
 
-if((enable-all OR enable-gsl) AND (NOT enable-lgpl) )
+if(enable-pthread)
+	set(MGL_HAVE_PTHREAD 1)
+	include(FindThreads)
+	if(NOT CMAKE_USE_PTHREADS_INIT)
+		message(SEND_ERROR "Couldn't find POSIX threads library.")
+	endif(NOT CMAKE_USE_PTHREADS_INIT)
+else(enable-pthread)
+	set(MGL_HAVE_PTHREAD 0)
+endif(enable-pthread)
+
+if(enable-gsl)
 	set(MGL_HAVE_GSL 1)
 	find_library(GSL_LIB gsl)
 	find_library(GSL_CBLAS_LIB gslcblas)
@@ -94,9 +147,9 @@ if((enable-all OR enable-gsl) AND (NOT enable-lgpl) )
 		message(SEND_ERROR "${GSL_INCLUDE_DIR}")
 		message(SEND_ERROR "Couldn't find GSL libraries.")
 	endif(NOT GSL_LIB OR NOT GSL_CBLAS_LIB OR NOT GSL_INCLUDE_DIR)
-else((enable-all OR enable-gsl) AND (NOT enable-lgpl) )
+else(enable-gsl)
 	set(MGL_HAVE_GSL 0)
-endif((enable-all OR enable-gsl) AND (NOT enable-lgpl) )
+endif(enable-gsl)
 
 #if(enable-all OR enable-ltdl)
 #	set(MGL_HAVE_LTDL 1)
@@ -111,17 +164,7 @@ endif((enable-all OR enable-gsl) AND (NOT enable-lgpl) )
 #	set(MGL_HAVE_LTDL 0)
 #endif(enable-all OR enable-ltdl)
 
-if(enable-all OR enable-pthread)
-	set(MGL_HAVE_PTHREAD 1)
-	include(FindThreads)
-	if(NOT CMAKE_USE_PTHREADS_INIT)
-		message(SEND_ERROR "Couldn't find POSIX threads library.")
-	endif(NOT CMAKE_USE_PTHREADS_INIT)
-else(enable-all OR enable-pthread)
-	set(MGL_HAVE_PTHREAD 0)
-endif(enable-all OR enable-pthread)
-
-if((enable-all OR enable-hdf4) AND (NOT enable-lgpl) )
+if(enable-hdf4)
 	set(MGL_HAVE_HDF4 1)
 	find_library(HDF4_LIB df)
 	find_library(HDF4MF_LIB mfhdf)
@@ -132,42 +175,43 @@ if((enable-all OR enable-hdf4) AND (NOT enable-lgpl) )
 		message(SEND_ERROR "${HDF4_INCLUDE_DIR}")
 		message(SEND_ERROR "Couldn't find HDF4 libraries.")
 	endif(NOT HDF4_LIB OR NOT HDF4MF_LIB OR NOT HDF4_INCLUDE_DIR)
-else((enable-all OR enable-hdf4) AND (NOT enable-lgpl) )
+else(enable-hdf4)
 	set(MGL_HAVE_HDF4 0)
-endif((enable-all OR enable-hdf4) AND (NOT enable-lgpl) )
+endif(enable-hdf4)
 
-if((enable-all OR enable-hdf5) AND (NOT enable-lgpl) )
+if(enable-hdf5)
+#	message(STATUS "enable hdf5")
 	set(MGL_HAVE_HDF5 1)
 	include(FindHDF5)
 	if(NOT HDF5_FOUND)
 		message(SEND_ERROR "Couldn't find HDF5 library.")
 	endif(NOT HDF5_FOUND)
-else((enable-all OR enable-hdf5) AND (NOT enable-lgpl) )
+else(enable-hdf5)
 	set(MGL_HAVE_HDF5 0)
-endif((enable-all OR enable-hdf5) AND (NOT enable-lgpl) )
+endif(enable-hdf5)
 
-if(enable-all OR enable-jpeg)
+if(enable-jpeg)
 	set(MGL_HAVE_JPEG 1)
 	include(FindJPEG)
 	if(NOT JPEG_FOUND)
 		message(SEND_ERROR "Couldn't find JPEG library.")
 	endif(NOT JPEG_FOUND)
-else(enable-all OR enable-jpeg)
+else(enable-jpeg)
 	set(MGL_HAVE_JPEG 0)
-endif(enable-all OR enable-jpeg)
+endif(enable-jpeg)
 
 
-if(enable-all OR enable-zlib)
+if(enable-zlib)
 	set(MGL_HAVE_ZLIB 1)
 	include(FindZLIB)
 	if(NOT ZLIB_FOUND)
 		message(SEND_ERROR "Couldn't find ZLib library.")
 	endif(NOT ZLIB_FOUND)
-else(enable-all OR enable-zlib)
+else(enable-zlib)
 	set(MGL_HAVE_ZLIB 0)
-endif(enable-all OR enable-zlib)
+endif(enable-zlib)
 
-if(enable-all OR enable-png)
+if(enable-png)
 	set(MGL_HAVE_PNG 1)
 	if(NOT MGL_HAVE_ZLIB)
 		message(SEND_ERROR "You have to enable ZLib if you plan to use PNG export.")
@@ -176,12 +220,12 @@ if(enable-all OR enable-png)
 	if(NOT PNG_FOUND)
 		message(SEND_ERROR "Couldn't find PNG library.")
 	endif(NOT PNG_FOUND)
-else(enable-all OR enable-png)
+else(enable-png)
 	set(MGL_HAVE_PNG 0)
-endif(enable-all OR enable-png)
+endif(enable-png)
 
 
-if( enable-all OR enable-pdf )
+if(enable-pdf)
 	set(MGL_HAVE_PDF 1)
 	if(NOT MGL_HAVE_PNG)
 		message(SEND_ERROR "You have to enable PNG if you plan to use PDF export.")
@@ -196,31 +240,31 @@ if( enable-all OR enable-pdf )
 	endif(NOT HPDF_INCLUDE_DIR)
 #	message(STATUS "Found libHaru library at: ${HPDF_LIB}")
 #	message(STATUS "Found libHaru headers: ${HPDF_INCLUDE_DIR}")
-else( enable-all OR enable-pdf )
+else(enable-pdf)
 	set(MGL_HAVE_PDF 0)
-endif( enable-all OR enable-pdf )
+endif(enable-pdf)
 
-if(enable-all OR enable-gif)
+if(enable-gif)
 	set(MGL_HAVE_GIF 1)
 	include(FindGIF)
 	if(NOT GIF_FOUND)
 		message(SEND_ERROR "Couldn't find GIF library.")
 	endif(NOT GIF_FOUND)
-else(enable-all OR enable-gif)
+else(enable-gif)
 	set(MGL_HAVE_GIF 0)
-endif(enable-all OR enable-gif)
+endif(enable-gif)
 
-if(enable-all OR enable-opengl)
+if(enable-opengl)
 	set(MGL_HAVE_OPENGL 1)
 	include(FindOpenGL)
 	if(NOT OPENGL_FOUND)
 		message(SEND_ERROR "Couldn't find OpenGL libraries.")
 	endif(NOT OPENGL_FOUND)
-else(enable-all OR enable-opengl)
+else(enable-opengl)
 	set(MGL_HAVE_OPENGL 0)
-endif(enable-all OR enable-opengl)
+endif(enable-opengl)
 
-if((enable-all-widgets OR enable-glut) AND (NOT enable-lgpl) )
+if(enable-glut)
 	set(MGL_HAVE_GLUT 1)
 	if(NOT MGL_HAVE_OPENGL)
 		message(SEND_ERROR "You have to enable OpenGL if you plan to use GLUT.")
@@ -229,44 +273,44 @@ if((enable-all-widgets OR enable-glut) AND (NOT enable-lgpl) )
 	if(NOT GLUT_FOUND)
 		message(SEND_ERROR "Couldn't find GLUT library.")
 	endif(NOT GLUT_FOUND)
-else((enable-all-widgets OR enable-glut) AND (NOT enable-lgpl) )
+else(enable-glut)
 	set(MGL_HAVE_GLUT 0)
-endif((enable-all-widgets OR enable-glut) AND (NOT enable-lgpl) )
+endif(enable-glut)
 
-if((enable-all-widgets OR enable-fltk) AND (NOT enable-lgpl) )
+if(enable-fltk)
 	set(MGL_HAVE_FLTK 1)
 	include(FindFLTK)
 	if(NOT FLTK_FOUND)
 		message(SEND_ERROR "Couldn't find FLTK library.")
 	endif(NOT FLTK_FOUND)
-else((enable-all-widgets OR enable-fltk) AND (NOT enable-lgpl) )
+else(enable-fltk)
 	set(MGL_HAVE_FLTK 0)
-endif((enable-all-widgets OR enable-fltk) AND (NOT enable-lgpl) )
+endif(enable-fltk)
 
 #if((enable-all-widgets OR enable-wx) AND (NOT enable-lgpl) )
-if((enable-wx) AND (NOT enable-lgpl) )
+if(enable-wx)
 	set(MGL_HAVE_WX 1)
 	FIND_PACKAGE(wxWidgets COMPONENTS base core gl)
 	if(NOT wxWidgets_FOUND)
 		message(SEND_ERROR "Couldn't find wxWidgets library.")
 	endif(NOT wxWidgets_FOUND)
 #else((enable-all-widgets OR enable-wx) AND (NOT enable-lgpl) )
-else((enable-wx) AND (NOT enable-lgpl) )
+else(enable-wx)
 	set(MGL_HAVE_WX 0)
 #endif((enable-all-widgets OR enable-wx) AND (NOT enable-lgpl) )
-endif((enable-wx) AND (NOT enable-lgpl) )
+endif(enable-wx)
 
-if((enable-all-widgets OR enable-qt) AND (NOT enable-lgpl) )
+if(enable-qt)
 	set(MGL_HAVE_QT 1)
-	FIND_PACKAGE(Qt4)
+	FIND_PACKAGE(Qt4 4.8 REQUIRED QtCore QtGui QtNetwork QtWebKit)
 	if(NOT QT4_FOUND)
 		message(SEND_ERROR "Couldn't find Qt4 library.")
 	endif(NOT QT4_FOUND)
-else((enable-all-widgets OR enable-qt) AND (NOT enable-lgpl) )
+else(enable-qt)
 	set(MGL_HAVE_QT 0)
-endif((enable-all-widgets OR enable-qt) AND (NOT enable-lgpl) )
+endif(enable-qt)
 
-if((enable-all-swig OR enable-python) AND (NOT enable-lgpl) )
+if(enable-python)
 	set(MGL_HAVE_PYTHON 1)
 	FIND_PACKAGE(PythonInterp)
 	if(NOT PYTHONINTERP_FOUND)
@@ -285,11 +329,11 @@ if((enable-all-swig OR enable-python) AND (NOT enable-lgpl) )
 	if(NOT NUMPY_INCLUDE_PATH)
 		message(SEND_ERROR "Couldn't find numpy.")
 	endif(NOT NUMPY_INCLUDE_PATH)
-else((enable-all-swig OR enable-python) AND (NOT enable-lgpl) )
+else(enable-python)
 	set(MGL_HAVE_PYTHON 0)
-endif((enable-all-swig OR enable-python) AND (NOT enable-lgpl) )
+endif(enable-python)
 
-if((enable-all-swig OR enable-octave) AND (NOT enable-lgpl) )
+if(enable-octave)
 	set(MGL_HAVE_OCTAVE 1)
 	find_program(oct_prog octave-config)
 	if(NOT oct_prog)
@@ -307,9 +351,9 @@ if((enable-all-swig OR enable-octave) AND (NOT enable-lgpl) )
 	if(NOT oct_tar)
 		message(SEND_ERROR "Couldn't find tar needed for octave interfaces creation.")
 	endif(NOT oct_tar)
-else((enable-all-swig OR enable-octave) AND (NOT enable-lgpl) )
+else(enable-octave)
 	set(MGL_HAVE_OCTAVE 0)
-endif((enable-all-swig OR enable-octave) AND (NOT enable-lgpl) )
+endif(enable-octave)
 
 if(enable-doc)
 	set(MGL_HAVE_DOC 1)
@@ -366,6 +410,7 @@ add_subdirectory( src )
 add_subdirectory( widgets )
 add_subdirectory( include )
 add_subdirectory( udav )
+add_subdirectory( json )
 #add_subdirectory( mgllab )
 add_subdirectory( lang )
 if(NOT MSVC AND NOT BORLAND)
diff --git a/ChangeLog.txt b/ChangeLog.txt
index c098daf..3e9aeb3 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,18 @@
+2.1.3 Released ?? April 2013
+
+* Improve SinFFT, CosFFT and so on, to be multi-threaded
+* Use DFT instead of FFT if GSL support is disabled (much slow!).
+* Correctly read #QNAN values in data files
+* Speed up Dots() drawing
+* Add MGL commands 'origintick', 'tickshift'
+* JSON now use zlib if filename end at 'z' (like "test.jsonz")
+* Make separate libmgl-mpi
+* Add mglExprC for parsing formula with complex numbers
+* Add SetAutoRanges()
+* Add flag to disable tick labels at axis origin (see SetOriginTick())
+* Add JSON sample usage via QtWebKit
+* Bugfixes and memory leaks
+
 2.1.2 Released 28 January 2013
 
 * Exclude "local" functions from resulting library.
diff --git a/README b/README
index ef4c0eb..d6385c3 100644
--- a/README
+++ b/README
@@ -61,5 +61,5 @@ is straightforward).
 CONTACTS
 --------
 MathGL was written by Alexey Balakin. You can contact me at 
-balakin at appl.sci-nnov.ru. The latest version of MathGL can be found at 
+mathgl.abalakin at gmail.com . The latest version of MathGL can be found at
 the sourceforge.net page (http://mathgl.sourceforge.net).
diff --git a/brush.ods b/brush.ods
new file mode 100644
index 0000000..9b0be4e
Binary files /dev/null and b/brush.ods differ
diff --git a/clean-svn b/clean-svn
new file mode 100755
index 0000000..f0ba47a
--- /dev/null
+++ b/clean-svn
@@ -0,0 +1,3 @@
+find . -name '.svn' -print0 | xargs -0 rm -rf
+find . -name '*~' -print0 | xargs -0 rm -f
+rm ./clean-svn
diff --git a/examples/full_test.cpp b/examples/full_test.cpp
index cb4ede2..d88befc 100644
--- a/examples/full_test.cpp
+++ b/examples/full_test.cpp
@@ -19,6 +19,7 @@
  ***************************************************************************/
 #include <time.h>
 #include <locale.h>
+#include <time.h>
 #if !defined(_MSC_VER) && !defined(__BORLANDC__)
 #include <getopt.h>
 #endif
@@ -61,12 +62,13 @@ void mgls_prepare2v(mglData *a, mglData *b);
 void mgls_prepare3v(mglData *ex, mglData *ey, mglData *ez);
 //-----------------------------------------------------------------------------
 void save(mglGraph *gr,const char *name,const char *suf);
+void smgl_stfa(mglGraph *gr);	// STFA sample
 void test(mglGraph *gr)
 {
 	mglParse par;
 	par.AllowSetSize(true);
 	setlocale(LC_CTYPE, "");
-	par.Execute(gr,"text 0 0 'test':text 0 1 'test='1+1:text 0 -1 'test='[1,3]");
+	par.Execute(gr,"xrange 1362565462 1365935062:ticktime 'x' 7*86400 '%d/%m/%y':axis");
 //	FILE *fp=fopen("/home/balakin/progr/mathgl-code/mathgl-2x/build/test.mgl","r");
 //	par.Execute(gr,fp,true);
 //	fclose(fp);
@@ -99,6 +101,7 @@ static struct option longopts[] =
 	{ "stl",	no_argument,	&type,		13 },
 	{ "tex",	no_argument,	&type,		14 },
 	{ "json",	no_argument,	&type,		15 },
+	{ "jsonz",	no_argument,	&type,		16 },
 	{ "test",	no_argument,	&dotest,	1 },
 	{ "font",	no_argument,	&dotest,	2 },
 	{ "thread",	required_argument,	NULL,	't' },
@@ -121,6 +124,7 @@ void usage()
 		"--eps		- output LaTeX\n"
 		"--jpeg		- output JPEG\n"
 		"--json		- output JSON\n"
+		"--jsonz		- output JSONz\n"
 		"--solid	- output solid PNG\n"
 		"--svg		- output SVG\n"
 		"--obj		- output obj/mtl\n"
@@ -147,53 +151,56 @@ void save(mglGraph *gr,const char *name,const char *suf="")
 	switch(type)
 	{
 		case 1:	// EPS
-			sprintf(buf,"%s%s.eps",name,suf);
+			snprintf(buf,128,"%s%s.eps",name,suf);
 			gr->WriteEPS(buf);
 			break;
 		case 2:	// SVG
-			sprintf(buf,"%s%s.svg",name,suf);
+			snprintf(buf,128,"%s%s.svg",name,suf);
 			gr->WriteSVG(buf);	break;
 		case 3:	// PNG
-			sprintf(buf,"%s%s.png",name,suf);
+			snprintf(buf,128,"%s%s.png",name,suf);
 			gr->WritePNG(buf,0,true);	break;
 		case 4:	// JPEG
-			sprintf(buf,"%s%s.jpg",name,suf);
+			snprintf(buf,128,"%s%s.jpg",name,suf);
 			gr->WriteJPEG(buf);	break;
 		case 5:	// PRC
-			sprintf(buf,"%s%s.prc",name,suf);
+			snprintf(buf,128,"%s%s.prc",name,suf);
 			gr->WritePRC(buf,"",false);	break;
 		case 6:	// GIF
-			sprintf(buf,"%s%s.gif",name,suf);
+			snprintf(buf,128,"%s%s.gif",name,suf);
 			gr->WriteGIF(buf);	break;
 		case 7:	// none
 			break;
 		case 8:	// EPS to PNG
-			sprintf(buf,"%s%s.png",name,suf);
+			snprintf(buf,128,"%s%s.png",name,suf);
 			gr->WritePNG(buf,0,false);
 			break;
  		case 9:	// PDF
-			sprintf(buf,"%s%s.prc",name,suf);
+			snprintf(buf,128,"%s%s.prc",name,suf);
 			gr->WritePRC(buf);	remove(buf);	break;
 		case 10:	// old OBJ
-			sprintf(buf,"%s%s.obj",name,suf);
+			snprintf(buf,128,"%s%s.obj",name,suf);
 			gr->WriteOBJold(buf);	break;
 		case 11:	// OBJ
-			sprintf(buf,"%s%s.obj",name,suf);
+			snprintf(buf,128,"%s%s.obj",name,suf);
 			gr->WriteOBJ(buf);	break;
 		case 12:	// OFF
-			sprintf(buf,"%s%s.off",name,suf);
+			snprintf(buf,128,"%s%s.off",name,suf);
 			gr->WriteOFF(buf);	break;
 		case 13:	// STL
-			sprintf(buf,"%s%s.stl",name,suf);
+			snprintf(buf,128,"%s%s.stl",name,suf);
 			gr->WriteSTL(buf);	break;
 		case 14:	// TeX
-			sprintf(buf,"%s%s.tex",name,suf);
+			snprintf(buf,128,"%s%s.tex",name,suf);
 			gr->WriteTEX(buf);	break;
 		case 15:	// JSON
-			sprintf(buf,"%s%s.json",name,suf);
+			snprintf(buf,128,"%s%s.json",name,suf);
+			gr->WriteJSON(buf);	break;
+		case 16:	// JSON
+			snprintf(buf,128,"%s%s.jsonz",name,suf);
 			gr->WriteJSON(buf);	break;
 		default:// PNG (no alpha)
-			sprintf(buf,"%s%s.png",name,suf);
+			snprintf(buf,128,"%s%s.png",name,suf);
 			gr->WritePNG(buf,0,false);	break;
 	}
 }
@@ -203,6 +210,7 @@ int main(int argc,char **argv)
 	const char *suf = "";
 	char name[256]="", *tmp;
 	int ch;
+	time_t st,en;	time(&st);
 	mglGraph *gr = NULL;
 	mglSample *s=samp;
 #if !defined(_MSC_VER) && !defined(__BORLANDC__)
@@ -212,11 +220,11 @@ int main(int argc,char **argv)
 			case 0:		break;
 			case 'w':	width =atoi(optarg);	break;
 			case 'h':	height=atoi(optarg);	break;
-			case 'k':	strcpy(name, optarg);
+			case 'k':	strncpy(name, optarg,256);
 						tmp=strchr(name,'.');	if(tmp)	*tmp=0;
 						tmp=strchr(name,'-');	if(tmp)	*tmp=0;
 						break;
-			case 't':	mglNumThr=atoi(optarg);	break;
+			case 't':	mgl_set_num_thr(atoi(optarg));	break;
 			case 'l':
 				while(s->name[0])	{	printf("%s ",s->name);	s++;	}
 				printf("\n");	return 0;
@@ -236,6 +244,7 @@ int main(int argc,char **argv)
 	if(dotest==1)
 	{
 		mgl_set_test_mode(true);	test(gr);
+		time(&en);	printf("time is %g sec\n",difftime(en,st));
 		gr->WritePNG("test.png","",false);
 		gr->WriteEPS("test.eps");
 		printf("Messages:%s\n",gr->Message());
@@ -246,7 +255,7 @@ int main(int argc,char **argv)
 	{	mgl_create_cpp_font(gr->Self(), L"!-~,¡-ÿ,̀-̏,Α-ω,ϑ,ϕ,ϖ,ϰ,ϱ,ϵ,А-я,ℏ,ℑ,ℓ,ℜ,←-↙,∀-∯,≠-≯,⟂");
 		delete gr;	return 0;	}
 
-	if(type==15)	mini=1;	// save mini version for json
+	if(type==15 || type==16)	mini=1;	// save mini version for json
 	
 	if(srnd)	mgl_srnd(1);
 	gr->VertexColor(false);	gr->Compression(false);
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 12f30ef..b868776 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -28,7 +28,7 @@ if(MGL_HAVE_QT)
 endif(MGL_HAVE_QT)
 
 #if(MGL_HAVE_FLTK AND MGL_HAVE_WX AND MGL_HAVE_QT)
-if(MGL_HAVE_FLTK AND MGL_HAVE_QT)
+if(MGL_HAVE_FLTK AND MGL_HAVE_QT AND MGL_HAVE_WX)
 	install(FILES mgl2/window.h DESTINATION ${MGL_INCLUDE_PATH})
 #endif(MGL_HAVE_FLTK AND MGL_HAVE_WX AND MGL_HAVE_QT)
-endif(MGL_HAVE_FLTK AND MGL_HAVE_QT)
+endif(MGL_HAVE_FLTK AND MGL_HAVE_QT AND MGL_HAVE_WX)
diff --git a/include/mgl2/addon.h b/include/mgl2/addon.h
index cda391a..37036d6 100644
--- a/include/mgl2/addon.h
+++ b/include/mgl2/addon.h
@@ -23,9 +23,12 @@
 #include "mgl2/define.h"
 #ifdef __cplusplus
 //-----------------------------------------------------------------------------
+/// Get integer power of x
 dual MGL_EXPORT mgl_ipowc(dual x,int n);
-dual expi(dual a);
-dual expi(double a);
+/// Get exp(i*a)
+dual MGL_EXPORT mgl_expi(dual a);
+/// Get exp(i*a)
+dual MGL_EXPORT mgl_expi(double a);
 
 /// Explicit scheme for 1 step of axial diffraction
 bool MGL_EXPORT mgl_difr_axial(dual *a, int n, dual q, int Border,dual *b, dual *d, int kk, double di);
@@ -34,8 +37,16 @@ bool MGL_EXPORT mgl_difr_grid(dual *a,int n,dual q,int Border,dual *b,dual *d,in
 //-----------------------------------------------------------------------------
 extern "C" {
 #endif
+/// Set seed for random numbers
+void MGL_EXPORT mgl_srnd(long seed);
+/// Get random number
+double MGL_EXPORT mgl_rnd();
+/// Get integer power of x
+double MGL_EXPORT mgl_ipow(double x,int n);
 
+/// Get random number with Gaussian distribution
 double MGL_EXPORT mgl_gauss_rnd();
+/// Fill frequencies for FFT
 void MGL_EXPORT mgl_fft_freq(double *freq,size_t nn);
 
 /// Remove double spaces from the string
diff --git a/include/mgl2/base.h b/include/mgl2/base.h
index acb5b21..5f0d9df 100644
--- a/include/mgl2/base.h
+++ b/include/mgl2/base.h
@@ -36,11 +36,13 @@ class mglBase;
 class mglData;
 class mglParser;
 class mglFormula;
+class mglFormulaC;
 class mglFont;
-typedef mglBase*  HMGL;
+typedef mglBase* HMGL;
 typedef mglData* HMDT;
 typedef mglParser* HMPR;
 typedef mglFormula* HMEX;
+typedef mglFormulaC* HAEX;
 //-----------------------------------------------------------------------------
 #if MGL_NO_DATA_A
 #define mglDataA mglData
@@ -74,7 +76,7 @@ public:
 #endif
 typedef const mglDataA* HCDT;
 //-----------------------------------------------------------------------------
-inline mreal MGL_EXPORT mgl_d(mreal v,mreal v1,mreal v2) { return v2!=v1?(v-v1)/(v2-v1):NAN; }
+inline mreal mgl_d(mreal v,mreal v1,mreal v2) { return v2!=v1?(v-v1)/(v2-v1):NAN; }
 //-----------------------------------------------------------------------------
 mglPoint GetX(HCDT x, int i, int j, int k=0);
 mglPoint GetY(HCDT y, int i, int j, int k=0);
@@ -145,7 +147,7 @@ inline mglPnt operator*(float b, const mglPnt &a)
 	c.r*=b;	c.g*=b;	c.b*=b;	c.a*=b;	return c;	}
 //-----------------------------------------------------------------------------
 /// Structure for glyph representation
-struct mglGlyph
+struct MGL_EXPORT mglGlyph
 {
 	long nt, nl;			///< number of triangles and lines
 	short *trig, *line;	///< vertexes of triangles and lines
@@ -162,10 +164,11 @@ struct mglGlyph
 		memcpy(line, a.line, 2*nl*sizeof(short));	return *this;	}
 };
 //-----------------------------------------------------------------------------
+#define MGL_TEXTURE_COLOURS 512
 /// Structure for texture (color scheme + palette) representation
 struct MGL_EXPORT mglTexture
 {
-	mglColor col[512];	///< Colors itself
+	mglColor col[MGL_TEXTURE_COLOURS];	///< Colors itself
 	long n;				///< Number of initial colors along u
 
 	char Sch[260];		///< Color scheme used
@@ -181,7 +184,7 @@ struct MGL_EXPORT mglTexture
 	void GetC(mreal u,mreal v,mglPnt &p) const;
 	mglColor GetC(mreal u,mreal v=0) const;
 	inline bool IsSame(const mglTexture &t) const
-	{	return n==t.n && !memcmp(col,t.col,512*sizeof(mglColor));	}
+	{	return n==t.n && !memcmp(col,t.col,MGL_TEXTURE_COLOURS*sizeof(mglColor));	}
 	void GetRGBA(unsigned char *f) const;
 	void GetRGBAPRC(unsigned char *f) const;
 	void GetRGBAOBJ(unsigned char *f) const;	// Export repeating border colors, since OBJ by default wraps textures and we need an extra boundary to work around implementation quirks
@@ -437,6 +440,7 @@ public:
 	inline mreal mark_size()	{	return MarkSize*font_factor;	}
 //	inline char last_color()	{	return last_style[1];	}
 	inline const char *last_line()	{	return last_style;	}
+	void resort();	///< Resort primitives in creation order (need for export in 3D formats)
 
 protected:
 	mglPoint OMin;		///< Lower edge for original axis (before scaling)
@@ -525,6 +529,7 @@ bool MGL_EXPORT mgl_isboth(HCDT x, HCDT y, HCDT z, HCDT a);
 typedef void *HMGL;
 typedef void *HMDT;
 typedef void *HMEX;
+typedef void *HAEX;
 typedef void *HMPR;
 typedef const void *HCDT;
 #endif
diff --git a/include/mgl2/base_cf.h b/include/mgl2/base_cf.h
index 73b9e34..c0a5597 100644
--- a/include/mgl2/base_cf.h
+++ b/include/mgl2/base_cf.h
@@ -122,6 +122,9 @@ void MGL_EXPORT mgl_set_range_val_(uintptr_t *gr, const char *dir, mreal *v1, mr
 /// Set range in direction dir as minimal and maximal values of data a
 void MGL_EXPORT mgl_set_range_dat(HMGL gr, char dir, HCDT a, int add);
 void MGL_EXPORT mgl_set_range_dat_(uintptr_t *gr, const char *dir, uintptr_t *a, int *add,int);
+/// Set ranges for automatic variables
+void MGL_EXPORT mgl_set_auto_ranges(HMGL gr, double x1, double x2, double y1, double y2, double z1, double z2, double c1, double c2);
+void MGL_EXPORT mgl_set_auto_ranges_(uintptr_t *gr, mreal *x1, mreal *x2, mreal *y1, mreal *y2, mreal *z1, mreal *z2, mreal *c1, mreal *c2);
 /// Set axis range scaling -- simplified way to shift/zoom axis range -- need to redraw whole image!
 void MGL_EXPORT mgl_zoom_axis(HMGL gr, double x1,double y1,double z1,double c1,double x2,double y2,double z2,double c2);
 void MGL_EXPORT mgl_zoom_axis_(uintptr_t *gr, mreal *x1, mreal *y1, mreal *z1, mreal *c1, mreal *x2, mreal *y2, mreal *z2, mreal *c2);
diff --git a/include/mgl2/canvas.h b/include/mgl2/canvas.h
index 1e4ceb1..3b7f222 100644
--- a/include/mgl2/canvas.h
+++ b/include/mgl2/canvas.h
@@ -21,10 +21,6 @@
 #define MGL_CANVAS_H
 #include "mgl2/base.h"
 //-----------------------------------------------------------------------------
-#ifndef MGL_STACK_ENTRY
-#define MGL_STACK_ENTRY	10
-#endif
-//-----------------------------------------------------------------------------
 struct GifFileType;
 //-----------------------------------------------------------------------------
 /// Structure for transformation matrix
@@ -88,7 +84,7 @@ class mglCanvas;
 /// Structure for light source
 struct mglDrawReg
 {
-	unsigned PDef;
+	unsigned long PDef;
 	int ObjId;
 	mreal PenWidth, pPos;
 	int x1,x2,y1,y2;
@@ -172,10 +168,7 @@ using mglBase::Light;
 	int GetHeight() const	{	return Height;	}
 	/// Combine plots from 2 canvases. Result will be saved into this.
 	void Combine(const mglCanvas *gr);
-	/// Send graphical information to node id using MPI
-	void MPI_Send(int id);
-	/// Receive graphical information from node id using MPI
-	void MPI_Recv(int id);
+
 	inline mreal GetDelay() const	{	return Delay;	}
 	inline void SetDelay(mreal d)	{	Delay=d;	}
 
@@ -303,12 +296,16 @@ using mglBase::Light;
 	/// Get rotation angle for glyph
 	float GetGlyphPhi(const mglPnt &q, float phi);
 
+	// Following arrays are open for advanced users only. It is not recommended to change them directly
+	float *Z;			///< Height for given level in Z-direction (size 3*width*height)
+	unsigned char *C;	///< Picture for given level in Z-direction (size 3*4*width*height)
+	int *OI;			///< ObjId arrays (size width*height)
+	/// Plot point p with color c
+	void pnt_plot(long x,long y,mreal z,const unsigned char c[4], int obj_id);
+	
 protected:
 	mreal Delay;		///< Delay for animation in seconds
 	// NOTE: Z should be float for reducing space and for compatibility reasons
-	float *Z;			///< Height for given level in Z-direction
-	unsigned char *C;	///< Picture for given level in Z-direction
-	int *OI;			///< ObjId arrays
 	unsigned char *G4;	///< Final picture in RGBA format. Prepared in Finish().
 	unsigned char *G;	///< Final picture in RGB format. Prepared in Finish().
 	std::vector<mglDrawDat> DrwDat;	///< Set of ALL drawing data for each frames
@@ -373,15 +370,15 @@ protected:
 	mreal text_plot(long p,const wchar_t *text,const char *fnt,mreal size=-1,mreal sh=0,mreal  col=-('k'), bool rot=true);
 
 	void add_prim(mglPrim &a);	///< add primitive to list
-	void mark_draw(long p, char type, mreal size, mglDrawReg *d);
-	void arrow_draw(long p1, long p2, char st, mreal size, mglDrawReg *d);
-	virtual void line_draw(long p1, long p2, mglDrawReg *d);
-	virtual void trig_draw(long p1, long p2, long p3, bool anorm, mglDrawReg *d);
-	virtual void quad_draw(long p1, long p2, long p3, long p4, mglDrawReg *d);
-	virtual void pnt_draw(long p, mglDrawReg *d);
+	void mark_draw(const mglPnt &p, char type, mreal size, mglDrawReg *d);
+	void arrow_draw(const mglPnt &p1, const mglPnt &p2, char st, mreal size, mglDrawReg *d);
+	virtual void line_draw(const mglPnt &p1, const mglPnt &p2, mglDrawReg *d);
+	virtual void trig_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, bool anorm, mglDrawReg *d);
+	virtual void quad_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, const mglPnt &p4, mglDrawReg *d);
+	virtual void pnt_draw(const mglPnt &p, mglDrawReg *d);
 	void arrow_draw(long n1, long n2, char st, float ll);
-	void arrow_plot_3d(long p1, long p2, char st, float ll);
-	void glyph_draw(const mglPrim *P, mglDrawReg *d);
+	void arrow_plot_3d(long n1, long n2, char st, float ll);
+	void glyph_draw(const mglPrim &P, mglDrawReg *d);
 	bool IsSame(const mglPrim &pr,mreal wp,mglColor cp,int st);
 
 	// restore normalized coordinates from screen ones
@@ -402,7 +399,7 @@ protected:
 	
 private:
 //	mreal _tetx,_tety,_tetz;		// extra angles
-	std::vector<mglMatrix> stack;	///< stack for transformation matrixes
+	std::vector<mglMatrix> stack;	///< stack for transformation matrices
 	int dr_nx1, dr_nx2, dr_ny1, dr_ny2;	// Allowed drawing region
 	GifFileType *gif;
 	mreal fscl,ftet;	///< last scale and rotation for glyphs
@@ -417,15 +414,13 @@ private:
 	char GetLabelPos(mreal c, long kk, mglAxis &aa);
 	/// Draw tick
 	void tick_draw(mglPoint o, mglPoint d1, mglPoint d2, int f, const char *stl);
-	/// Plot point p with color c
-	void pnt_plot(long x,long y,mreal z,const unsigned char c[4], int obj_id);
 	mreal FindOptOrg(char dir, int ind) const;
 	/// Transform mreal color and alpha to bits format
 	unsigned char* col2int(const mglPnt &p, unsigned char *r, int obj_id);
 	/// Combine colors in 2 plane.
 	void combine(unsigned char *c1, const unsigned char *c2);
 	/// Fast drawing of line between 2 points
-	void fast_draw(long p1, long p2, mglDrawReg *d);
+	void fast_draw(const mglPnt &p1, const mglPnt &p2, mglDrawReg *d);
 
 	/// Additionally scale points p for positioning in image
 	void PostScale(mglPoint &p) const;
diff --git a/include/mgl2/canvas_cf.h b/include/mgl2/canvas_cf.h
index a2f0df7..db02211 100644
--- a/include/mgl2/canvas_cf.h
+++ b/include/mgl2/canvas_cf.h
@@ -351,13 +351,6 @@ void MGL_EXPORT mgl_view_(uintptr_t *gr, mreal *TetX, mreal *TetZ, mreal *TetY);
 void MGL_EXPORT mgl_zoom(HMGL gr, double x1, double y1, double x2, double y2);
 void MGL_EXPORT mgl_zoom_(uintptr_t *gr, mreal *x1, mreal *y1, mreal *x2, mreal *y2);
 
-/// Send graphical information to node id using MPI
-void MGL_EXPORT mgl_mpi_send(HMGL gr, int id);
-void MGL_EXPORT mgl_mpi_send_(uintptr_t *gr, int *id);
-/// Receive graphical information from node id using MPI
-void MGL_EXPORT mgl_mpi_recv(HMGL gr, int id);
-void MGL_EXPORT mgl_mpi_recv_(uintptr_t *gr, int *id);
-
 //-----------------------------------------------------------------------------
 void MGL_EXPORT mgl_draw_thr(void *);
 /// Callback function for mouse click
@@ -490,14 +483,18 @@ HMDT MGL_EXPORT mgl_parser_calcw(HMPR pr, const wchar_t *formula);
 /// Create HMEX object for expression evaluating
 HMEX MGL_EXPORT mgl_create_expr(const char *expr);
 uintptr_t MGL_EXPORT mgl_create_expr_(const char *expr, int);
+HAEX MGL_EXPORT mgl_create_cexpr(const char *expr);
 /// Delete HMEX object
 void MGL_EXPORT mgl_delete_expr(HMEX ex);
 void MGL_EXPORT mgl_delete_expr_(uintptr_t *ex);
+void MGL_EXPORT mgl_delete_cexpr(HAEX ex);
 /// Return value of expression for given x,y,z variables
 double MGL_EXPORT mgl_expr_eval(HMEX ex, double x, double y,double z);
 double MGL_EXPORT mgl_eval_expr_(uintptr_t *ex, mreal *x, mreal *y, mreal *z);
+dual MGL_EXPORT mgl_cexpr_eval(HAEX ex, dual x, dual y,dual z);
 /// Return value of expression for given variables
 double MGL_EXPORT mgl_expr_eval_v(HMEX ex, mreal *var);
+dual MGL_EXPORT mgl_cexpr_eval_v(HAEX ex, dual *var);
 /// Return value of expression differentiation over variable dir for given x,y,z variables
 double MGL_EXPORT mgl_expr_diff(HMEX ex, char dir, double x, double y,double z);
 double MGL_EXPORT mgl_diff_expr_(uintptr_t *ex, const char *dir, mreal *x, mreal *y, mreal *z, int);
diff --git a/include/mgl2/data.h b/include/mgl2/data.h
index 06a1fd1..c483e8f 100644
--- a/include/mgl2/data.h
+++ b/include/mgl2/data.h
@@ -332,14 +332,14 @@ public:
 	/// Interpolate by cubic spline the data and return its derivatives at given point x,\a y,\a z which normalized in range [0, 1]
 	inline mreal Spline1(mglPoint &dif, mreal x,mreal y=0,mreal z=0) const
 	{	mreal res=mgl_data_spline_ext(this, x*(nx-1),y*(ny-1),z*(nz-1), &(dif.x),&(dif.y), &(dif.z));
-		dif.x/=nx>1?nx-1:1;	dif.y/=ny>1?ny-1:1;	dif.z/=nz>1?nz-1:1;	return res;	}
+		dif.x*=nx>1?nx-1:1;	dif.y*=ny>1?ny-1:1;	dif.z*=nz>1?nz-1:1;	return res;	}
 	/// Interpolate by linear function the data and return its derivatives at given point x=[0...nx-1], y=[0...ny-1], z=[0...nz-1]
 	inline mreal Linear(mglPoint &dif, mreal x,mreal y=0,mreal z=0)	const
 	{	return mgl_data_linear_ext(this,x,y,z, &(dif.x),&(dif.y), &(dif.z));	}
 	/// Interpolate by line the data and return its derivatives at given point x,\a y,\a z which normalized in range [0, 1]
 	inline mreal Linear1(mglPoint &dif, mreal x,mreal y=0,mreal z=0) const
 	{	mreal res=mgl_data_linear_ext(this,x*(nx-1),y*(ny-1),z*(nz-1), &(dif.x),&(dif.y), &(dif.z));
-		dif.x/=nx>1?nx-1:1;	dif.y/=ny>1?ny-1:1;	dif.z/=nz>1?nz-1:1;	return res;	}
+		dif.x*=nx>1?nx-1:1;	dif.y*=ny>1?ny-1:1;	dif.z*=nz>1?nz-1:1;	return res;	}
 	
 	/// Get information about the data (sizes and momentum) to string
 	inline const char *PrintInfo() const	{	return mgl_data_info(this);	}
diff --git a/include/mgl2/data_cf.h b/include/mgl2/data_cf.h
index 2c36b88..a1bf01c 100644
--- a/include/mgl2/data_cf.h
+++ b/include/mgl2/data_cf.h
@@ -26,8 +26,13 @@
 #include <gsl/gsl_vector.h>
 #include <gsl/gsl_matrix.h>
 #else
+#ifdef __cplusplus
 struct gsl_vector;
 struct gsl_matrix;
+#else
+typedef void gsl_vector;
+typedef void gsl_matrix;
+#endif
 #endif
 //-----------------------------------------------------------------------------
 #ifdef __cplusplus
@@ -42,7 +47,7 @@ double MGL_EXPORT mgl_rnd();
 double MGL_EXPORT mgl_rnd_();
 /// Get integer power of x
 double MGL_EXPORT mgl_ipow(double x,int n);
-double MGL_EXPORT mgl_ipow_(double *x,int *n);
+double MGL_EXPORT mgl_ipow_(mreal *x,int *n);
 /// Get number of seconds since 1970 for given string
 double MGL_EXPORT mgl_get_time(const char *time, const char *fmt);
 double MGL_EXPORT mgl_get_time_(const char *time, const char *fmt,int,int);
@@ -67,7 +72,7 @@ void MGL_EXPORT mgl_data_rearrange(HMDT dat, long mx,long my,long mz);
 void MGL_EXPORT mgl_data_rearrange_(uintptr_t *dat, int *mx, int *my, int *mz);
 /// Link external data array (don't delete it at exit)
 void MGL_EXPORT mgl_data_link(HMDT dat, mreal *A,long mx,long my,long mz);
-void MGL_EXPORT mgl_data_link_(uintptr_t *d, float *A, int *nx,int *ny,int *nz);
+void MGL_EXPORT mgl_data_link_(uintptr_t *d, mreal *A, int *nx,int *ny,int *nz);
 /// Allocate memory and copy the data from the (float *) array
 void MGL_EXPORT mgl_data_set_float(HMDT dat, const float *A,long mx,long my,long mz);
 void MGL_EXPORT mgl_data_set_float_(uintptr_t *dat, const float *A,int *NX,int *NY,int *NZ);
@@ -302,6 +307,13 @@ void MGL_EXPORT mgl_data_cosfft_(uintptr_t *dat, const char *dir,int);
 void MGL_EXPORT mgl_data_fill_sample(HMDT dat, const char *how);
 void MGL_EXPORT mgl_data_fill_sample_(uintptr_t *dat, const char *how,int);
 
+/// Allocate and prepare data for Fourier transform by nthr threads
+MGL_EXPORT void *mgl_fft_alloc(long n, void **space, long nthr);
+/// Free data for Fourier transform
+void MGL_EXPORT mgl_fft_free(void *wt, void **ws, long nthr);
+/// Make Fourier transform of data x of size n and step s between points
+void MGL_EXPORT mgl_fft(double *x, long s, long n, const void *wt, void *ws, bool inv);
+
 /// Interpolate by cubic spline the data to given point x=[0...nx-1], y=[0...ny-1], z=[0...nz-1]
 mreal MGL_EXPORT mgl_data_spline(HCDT dat, mreal x,mreal y,mreal z);
 mreal MGL_EXPORT mgl_data_spline_(uintptr_t *dat, mreal *x,mreal *y,mreal *z);
@@ -391,6 +403,8 @@ uintptr_t MGL_EXPORT mgl_qo2d_solve_(const char *ham, uintptr_t* ini_re, uintptr
 HMDT MGL_EXPORT mgl_qo3d_solve(const char *ham, HCDT ini_re, HCDT ini_im, HCDT ray, mreal r, mreal k0, HMDT xx, HMDT yy, HMDT zz);
 HMDT MGL_EXPORT mgl_qo3d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal z, mreal px, mreal py, mreal pz, void *par), void *par, HCDT ini_re, HCDT ini_im, HCDT ray, mreal r, mreal k0, HMDT xx, HMDT yy, HMDT zz);
 uintptr_t MGL_EXPORT mgl_qo3d_solve_(const char *ham, uintptr_t* ini_re, uintptr_t* ini_im, uintptr_t* ray, mreal *r, mreal *k0, uintptr_t* xx, uintptr_t* yy, uintptr_t* zz, int);
+/// Saves result of ODE solving of n equations with right part func and initial conditions x0 over time interval [0,tmax] with time step dt
+HMDT MGL_EXPORT mgl_ode_solve(void (*func)(const mreal *x, mreal *dx, void *par), int n, mreal *x0, mreal dt, mreal tmax, void *par);
 /// Finds ray with starting point r0, p0 (and prepares ray data for mgl_qo2d_solve)
 HMDT MGL_EXPORT mgl_ray_trace(const char *ham, mreal x0, mreal y0, mreal z0, mreal px, mreal py, mreal pz, mreal dt, mreal tmax);
 uintptr_t MGL_EXPORT mgl_ray_trace_(const char *ham, mreal *x0, mreal *y0, mreal *z0, mreal *px, mreal *py, mreal *pz, mreal *dt, mreal *tmax,int);
diff --git a/include/mgl2/datac.h b/include/mgl2/datac.h
index ea96d47..d222509 100644
--- a/include/mgl2/datac.h
+++ b/include/mgl2/datac.h
@@ -183,6 +183,9 @@ public:
 	/// Read data from text file with size specified at beginning of the file
 	inline bool ReadMat(const char *fname, long dim=2)
 	{	return mgl_datac_read_mat(this,fname,dim);	}
+	/// Export data array (for ns=-1) or only ns-th slice to PNG file according color scheme
+	inline void Export(const char *fname,const char *scheme,mreal v1=0,mreal v2=0,long ns=-1) const
+	{	mgl_data_export(this,fname,scheme,v1,v2,ns);	}
 
 		/// Read data array from HDF file (parse HDF4 and HDF5 files)
 	inline int ReadHDF(const char *fname,const char *data)
diff --git a/include/mgl2/define.h b/include/mgl2/define.h
index 47d823d..aa23043 100644
--- a/include/mgl2/define.h
+++ b/include/mgl2/define.h
@@ -23,7 +23,7 @@
 #include "mgl2/config.h"
 #include "mgl2/dllexport.h"
 
-#define MGL_VER2 	1.2
+#define MGL_VER2 	1.3
 //-----------------------------------------------------------------------------
 #ifdef WIN32 //_MSC_VER needs this before math.h
 #define	_USE_MATH_DEFINES
@@ -61,6 +61,7 @@
 #define hypot	_hypot
 #define getcwd	_getcwd
 #define chdir	_chdir // BORLAND has chdir
+#define snprintf _snprintf
 #endif
 
 #if defined(_MSC_VER) || defined(__BORLANDC__)
@@ -100,10 +101,6 @@ typedef float mreal;
 #define MGL_CMAP_COLOR	32
 #endif
 //-----------------------------------------------------------------------------
-#ifndef MGL_STACK_ENTRY
-#define MGL_STACK_ENTRY	10
-#endif
-//-----------------------------------------------------------------------------
 #ifndef MGL_DEF_VIEWER
 #define MGL_DEF_VIEWER "evince"
 #endif
@@ -184,7 +181,7 @@ enum{	// Codes for warnings/messages
 #define MGL_DISABLE_SCALE	0x000200 	///< Temporary flag for disable scaling (used for axis)
 #define MGL_FINISHED 		0x000400 	///< Flag that final picture (i.e. mglCanvas::G) is ready
 #define MGL_USE_GMTIME		0x000800 	///< Use gmtime instead of localtime
-#define MGL_SHOW_POS			0x001000 	///< Switch to show or not mouse click position
+#define MGL_SHOW_POS		0x001000 	///< Switch to show or not mouse click position
 #define MGL_CLF_ON_UPD		0x002000 	///< Clear plot before Update()
 #define MGL_NOSUBTICKS		0x004000 	///< Disable subticks drawing (for bounding box)
 #define MGL_DIFFUSIVE		0x008000 	///< Use diffusive light instead of specular
@@ -192,7 +189,7 @@ enum{	// Codes for warnings/messages
 #define MGL_REDUCEACC		0x020000 	///< Reduce accuracy of points (to reduc size of output files)
 #define MGL_PREFERVC 		0x040000 	///< Prefer vertex color instead of texture if output format supports
 #define MGL_ONESIDED 		0x080000 	///< Render only front side of surfaces if output format supports (for debugging)
-//#define MGL_3D_ARROW 		0x100000 	///< Use 3D arrows instead of 2d ones
+#define MGL_NO_ORIGIN 		0x100000 	///< Don't draw tick labels at axis origin
 //-----------------------------------------------------------------------------
 #ifdef __cplusplus
 //-----------------------------------------------------------------------------
@@ -230,17 +227,30 @@ struct mglThreadV
 	int id;			// thread id
 	long n;			// total number of iteration
 };
+struct mglThreadT
+{
+	void *a; 		// dual* or mreal* array with input or results
+	double *b; 		// dual* array with input or results
+	const long *p;	// long* array with parameters
+	const void *v;	// pointer to table/parameter
+	void **w; 		// pointer to workspace
+	int id;			// thread id
+	long n;			// total number of iteration
+	const void *re,*im;
+};
 /// Start several thread for the task
-void mglStartThread(void *(*func)(void *), void (*post)(mglThreadD *,mreal *), long n,
+void MGL_EXPORT mglStartThread(void *(*func)(void *), void (*post)(mglThreadD *,mreal *), long n,
 					mreal *a=0, const mreal *b=0, const mreal *c=0, const long *p=0,
 					const void *v=0, const mreal *d=0, const mreal *e=0, const char *s=0);
-void mglStartThreadV(void *(*func)(void *), long n, mreal *a, const void *b=0,
+void MGL_EXPORT mglStartThreadV(void *(*func)(void *), long n, mreal *a, const void *b=0,
 					const void *c=0, const long *p=0, const void *v=0, const mreal *d=0);
-void mglStartThreadV(void *(*func)(void *), long n, dual *a, const void *b=0,
+void MGL_EXPORT mglStartThreadV(void *(*func)(void *), long n, dual *a, const void *b=0,
 					const void *c=0, const long *p=0, const void *v=0, const mreal *d=0);
-void mglStartThreadC(void *(*func)(void *), void (*post)(mglThreadC *,dual *), long n,
+void MGL_EXPORT mglStartThreadC(void *(*func)(void *), void (*post)(mglThreadC *,dual *), long n,
 					dual *a=0, const dual *b=0, const dual *c=0, const long *p=0,
 					const void *v=0, const dual *d=0, const dual *e=0, const char *s=0);
+void MGL_EXPORT mglStartThreadT(void *(*func)(void *), long n, void *a, double *b, const void *v=0,
+					void **w=0, const long *p=0, const void *re=0, const void *im=0);
 MGL_EXPORT extern int mglNumThr;		///< Number of thread for plotting and data handling
 //-----------------------------------------------------------------------------
 extern "C" {
@@ -252,6 +262,8 @@ typedef double _Complex dual;
 typedef float _Complex dual;
 #endif
 #endif
+/// Get RGB values for given color id or fill by -1 if no one found
+void MGL_EXPORT mgl_chrrgb(char id, float rgb[3]);
 /// Check if string contain color id and return its number
 long MGL_EXPORT mgl_have_color(const char *stl);
 /// Find symbol in string excluding {} and return its position or NULL
diff --git a/include/mgl2/font.h b/include/mgl2/font.h
index 95a2356..1a6ab79 100644
--- a/include/mgl2/font.h
+++ b/include/mgl2/font.h
@@ -49,7 +49,7 @@ const float mgl_fgen = 4*14;
 char mglGetStyle(const char *how, int *font, int *align=0);
 class mglBase;
 //-----------------------------------------------------------------------------
-/// Class for incapsulating font plotting procedures
+/// Class for font typeface and text plotting procedures
 class mglFont
 {
 public:
diff --git a/include/mgl2/mgl.h b/include/mgl2/mgl.h
index f2af079..e7c8111 100644
--- a/include/mgl2/mgl.h
+++ b/include/mgl2/mgl.h
@@ -151,12 +151,18 @@ public:
 	/// Set values of axis range as minimal and maximal values of datas
 	inline void SetRanges(const mglDataA &xx, const mglDataA &yy)
 	{	mgl_set_range_dat(gr,'x',&xx,0);	mgl_set_range_dat(gr,'y',&yy,0);	}
-	/// Set values of axis range
+	/// Set values of axis ranges
 	inline void SetRanges(double x1, double x2, double y1, double y2, double z1=0, double z2=0)
 	{	mgl_set_ranges(gr, x1, x2, y1, y2, z1, z2);	}
-	/// Set values of axis range
+	/// Set values of axis ranges
 	inline void SetRanges(mglPoint p1, mglPoint p2)
 	{	mgl_set_ranges(gr, p1.x, p2.x, p1.y, p2.y, p1.z, p2.z);	}
+	/// Set ranges for automatic variables
+	inline void SetAutoRanges(double x1, double x2, double y1=0, double y2=0, double z1=0, double z2=0, double c1=0, double c2=0)
+	{	mgl_set_auto_ranges(gr, x1, x2, y1, y2, z1, z2, c1, c2);	}
+	/// Set ranges for automatic variables
+	inline void SetAutoRanges(mglPoint p1, mglPoint p2)
+	{	mgl_set_auto_ranges(gr, p1.x, p2.x, p1.y, p2.y, p1.z, p2.z, p1.c, p2.c);	}
 	/// Set axis origin
 	inline void SetOrigin(mglPoint p)
 	{	mgl_set_origin(gr, p.x, p.y, p.z);	}
@@ -215,6 +221,9 @@ public:
 	/// Set to use UTC time instead of local time
 	inline void SetTimeUTC(bool enable)
 	{	mgl_set_flag(gr,enable, MGL_USE_GMTIME);	}
+	/// Set to draw tick labels at axis origin
+	inline void SetOriginTick(bool enable=true)
+	{	mgl_set_flag(gr,!enable, MGL_NO_ORIGIN);	}
 	
 	/// Put further plotting in some region of whole frame.
 	inline void SubPlot(int nx,int ny,int m,const char *style="<>_^", double dx=0, double dy=0)
@@ -428,10 +437,6 @@ public:
 
 	/// Combine plots from 2 canvases. Result will be saved into this
 	inline void Combine(const mglGraph *g)	{	mgl_combine_gr(gr,g->gr);	}
-	/// Send graphical information to node id using MPI
-	inline void MPI_Send(int id)	{	mgl_mpi_send(gr,id);	}
-	/// Receive graphical information from node id using MPI
-	inline void MPI_Recv(int id)	{	mgl_mpi_recv(gr,id);	}
 
 	/// Clear up the frame
 	inline void Clf(double r, double g, double b)	{	mgl_clf_rgb(gr, r, g, b);	}
@@ -1297,4 +1302,28 @@ public:
 #endif
 };
 //-----------------------------------------------------------------------------
+/// Wrapper class expression evaluating
+class mglExprC
+{
+	HAEX ex;
+public:
+	mglExprC(const char *expr)		{	ex = mgl_create_cexpr(expr);	}
+	~mglExprC()	{	mgl_delete_cexpr(ex);	}
+	/// Return value of expression for given x,y,z variables
+	inline dual Eval(dual x, dual y=0, dual z=0)
+	{	return mgl_cexpr_eval(ex,x,y,z);	}
+	/// Return value of expression for given x,y,z,u,v,w variables
+	inline dual Eval(dual x, dual y, dual z, dual u, dual v, dual w)
+	{
+		dual var[26];
+		var['x'-'a']=x;	var['y'-'a']=y;	var['z'-'a']=z;
+		var['u'-'a']=u;	var['v'-'a']=v;	var['w'-'a']=w;
+		return mgl_cexpr_eval_v(ex,var);	}
+#ifndef SWIG
+	/// Return value of expression for given variables
+	inline dual Eval(dual var[26])
+	{	return mgl_cexpr_eval_v(ex,var);	}
+#endif
+};
+//-----------------------------------------------------------------------------
 #endif
diff --git a/examples/glut_example.cpp b/include/mgl2/mpi.h
similarity index 58%
copy from examples/glut_example.cpp
copy to include/mgl2/mpi.h
index 419bb74..4c456c9 100644
--- a/examples/glut_example.cpp
+++ b/include/mgl2/mpi.h
@@ -1,5 +1,5 @@
 /***************************************************************************
- * glut_example.cpp is part of Math Graphic Library
+ * mgl.h is part of Math Graphic Library
  * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>       *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -17,34 +17,39 @@
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
-#include "mgl2/glut.h"
-//-----------------------------------------------------------------------------
-int test_wnd(mglGraph *gr);
-int sample(mglGraph *gr);
-int sample_1(mglGraph *gr);
-int sample_2(mglGraph *gr);
-int sample_3(mglGraph *gr);
-int sample_d(mglGraph *gr);
+#ifndef _MGL_MPI_H_
+#define _MGL_MPI_H_
+
+#include "mgl2/mgl_cf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/// Send graphical information to node id using MPI
+void MGL_EXPORT mgl_mpi_send(HMGL gr, int id);
+void MGL_EXPORT mgl_mpi_send_(uintptr_t *gr, int *id);
+/// Receive graphical information from node id using MPI
+void MGL_EXPORT mgl_mpi_recv(HMGL gr, int id);
+void MGL_EXPORT mgl_mpi_recv_(uintptr_t *gr, int *id);
+#ifdef __cplusplus
+}
+
+#include "mgl2/mgl.h"
 //-----------------------------------------------------------------------------
-typedef int (*draw_func)(mglGraph *gr);
-int main(int argc,char **argv)
+/// Wrapper class for all graphics
+class mglGraphMPI:public mglGraph
 {
-	char key = 0;
-	if(argc>1)	key = argv[1][0]!='-' ? argv[1][0] : argv[1][1];
-	else	printf("You may specify argument '1', '2', '3' or 'd' for viewing examples of 1d, 2d, 3d or dual plotting\n");
+	inline mglGraphMPI(int kind=0, int width=600, int height=400):mglGraph(kind,width,height){}
+	inline mglGraphMPI(const mglGraph &graph):mglGraph(graph){}
+	inline mglGraphMPI(HMGL graph):mglGraph(graph){}
+	virtual ~mglGraphMPI(){}
 
-	const char *desc;
-	draw_func func;
-	switch(key)
-	{
-	case '1':	func = sample_1;	desc = "1D plots";	break;
-	case '2':	func = sample_2;	desc = "2D plots";	break;
-	case '3':	func = sample_3;	desc = "3D plots";	break;
-	case 'd':	func = sample_d;	desc = "Dual plots";	break;
-	case 't':	func = test_wnd;	desc = "Testing";	break;
-	default:	func = sample;	desc = "Example of molecules";	break;
-	}
-	mglGLUT gr(func,desc);
-	return 0;
-}
+	/// Send graphical information to node id using MPI
+	inline void MPI_Send(int id)	{	mgl_mpi_send(gr,id);	}
+	/// Receive graphical information from node id using MPI
+	inline void MPI_Recv(int id)	{	mgl_mpi_recv(gr,id);	}
+	
+};
+#endif
 //-----------------------------------------------------------------------------
+#endif
diff --git a/include/mgl2/opengl.h b/include/mgl2/opengl.h
index 501ceee..4b2c7ea 100644
--- a/include/mgl2/opengl.h
+++ b/include/mgl2/opengl.h
@@ -45,10 +45,15 @@ public:
 	void Clf(mglColor Back=WC);
 
 protected:
-	void line_draw(long p1, long p2, mglDrawReg *d);
-	void trig_draw(long p1, long p2, long p3, bool anorm, mglDrawReg *d);
-	void quad_draw(long p1, long p2, long p3, long p4, mglDrawReg *d);
-	void pnt_draw(long p, mglDrawReg *d);
+	// provide fastest variant for usual points (not glyphs or marks)
+	void line_draw(long n1, long n2);
+	void trig_draw(long n1, long n2, long n3);
+	void quad_draw(long n1, long n2, long n3, long n4);
+	// variant for glyphs or marks
+	void line_draw(const mglPnt &p1, const mglPnt &p2, mglDrawReg *d);
+	void trig_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, bool anorm, mglDrawReg *d);
+	void quad_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, const mglPnt &p4, mglDrawReg *d);
+	void pnt_draw(const mglPnt &p, mglDrawReg *d);
 
 	unsigned char **GetRGBLines(long &w, long &h, unsigned char *&f, bool solid=true);
 	void LightScale();
diff --git a/include/mgl2/qt.h b/include/mgl2/qt.h
index 4aaedb8..fb7f7c1 100644
--- a/include/mgl2/qt.h
+++ b/include/mgl2/qt.h
@@ -51,7 +51,7 @@ public:
 	int Run()	{	return mgl_qt_run();	}	///< Run main loop for event handling
 };
 //-----------------------------------------------------------------------------
-void mgl_ask_qt(const wchar_t *quest, wchar_t *res);
+void MGL_EXPORT mgl_ask_qt(const wchar_t *quest, wchar_t *res);
 //-----------------------------------------------------------------------------
 #endif
 #endif
diff --git a/include/mgl2/type.h b/include/mgl2/type.h
index 1379c16..fb803ed 100644
--- a/include/mgl2/type.h
+++ b/include/mgl2/type.h
@@ -33,7 +33,7 @@ const mreal mgl_min_a = 1./256;
 #define MGL_SET_RGBA(p,rr,gg,bb,aa)	{p.r=(rr);p.g=(gg);p.b=(bb);p.a=(aa);}
 #define MGL_SET_RGB(p,rr,gg,bb)		{p.r=(rr);p.g=(gg);p.b=(bb);}
 //-----------------------------------------------------------------------------
-/// Class for incapsulating point in space
+/// Class for point in 3D space
 struct mglPoint
 {
 	mreal x,y,z,c;
@@ -87,12 +87,8 @@ inline mreal mgl_norm(const mglPoint &p)
 {	return sqrt(p.x*p.x+p.y*p.y+p.z*p.z);	}
 #endif
 //-----------------------------------------------------------------------------
-/// Class for incapsulating color
-#ifndef SWIG
-struct MGL_EXPORT mglColor
-#else
+/// Class for RGBA color
 struct mglColor
-#endif
 {
 	float r;	///< Red component of color
 	float g;	///< Green component of color
@@ -101,12 +97,20 @@ struct mglColor
 
 	/// Constructor for RGB components manualy
 	mglColor(float R,float G,float B, float A=1){	r=R;	g=G;	b=B;	a=A;	}
+	/// Constructor set default color
+	mglColor()		{	r=g=b=0;	a=1;	}
 	/// Constructor set color from character id
-	mglColor(char c='k', float bright=1)		{	Set(c,bright);	}
+	mglColor(char c, float bright=1)		{	Set(c,bright);	}
 	/// Set color as Red, Green, Blue values
 	void Set(float R,float G,float B,float A=1)	{	r=R;	g=G;	b=B;	a=A;	}
 	/// Set color as Red, Green, Blue values
-	void Set(mglColor c, float bright=1);
+	void Set(mglColor c, float bright=1)
+	{
+		if(bright<0)	bright=0;	if(bright>2.f)	bright=2.f;
+		r = bright<=1 ? c.r*bright : 1 - (1-c.r)*(2-bright);
+		g = bright<=1 ? c.g*bright : 1 - (1-c.g)*(2-bright);
+		b = bright<=1 ? c.b*bright : 1 - (1-c.b)*(2-bright);	a = 1;
+	}
 	/// Check if color is valid
 	inline bool Valid()
 	{	return (r>=0 && r<=1 && g>=0 && g<=1 && b>=0 && b<=1 && a>=0 && a<=1);	}
@@ -116,7 +120,11 @@ struct mglColor
 	inline float NormS()
 	{	return r*r+g*g+b*b;	}
 	/// Set color from symbolic id
-	void Set(char p, float bright=1);
+	inline void Set(char p, float bright=1)
+	{
+		float rgb[3];	mgl_chrrgb(p,rgb);
+		Set(mglColor(rgb[0],rgb[1],rgb[2]),bright);
+	}
 	/// Copy color from other one
 	inline bool operator==(const mglColor &c) const
 	{	return !memcmp(this, &c, sizeof(mglColor));	}
diff --git a/json/Backend.cpp b/json/Backend.cpp
new file mode 100644
index 0000000..1e4d23c
--- /dev/null
+++ b/json/Backend.cpp
@@ -0,0 +1,77 @@
+#include <QMessageBox>
+#include <QTextStream>
+#include <QFile>
+#include <QDebug>
+#include "Backend.hpp"
+#include <mgl2/mgl.h>
+//-----------------------------------------------------------------------------
+Backend::Backend(QObject *parent) : QObject(parent) { }
+//-----------------------------------------------------------------------------
+QString Backend::show(const QString& text) const
+{
+	qDebug() << __FUNCTION__;
+	const char *tmp = tmpnam(0);
+	mglGraph gr;
+	gr.SetFaceNum(200);
+	mglParse pr;
+	pr.AllowSetSize(true);
+	setlocale(LC_CTYPE, "");
+	setlocale(LC_NUMERIC, "C");
+	pr.Execute(&gr,text.toAscii());
+	gr.WriteJSON(tmp);
+	setlocale(LC_NUMERIC, "");
+
+	QFile f(tmp);
+	f.open(QIODevice::ReadOnly);
+	QTextStream ts(&f);
+	ts.setAutoDetectUnicode(true);
+	const QString json = ts.readAll();
+	f.remove();
+	return json;
+}
+//-----------------------------------------------------------------------------
+QString Backend::coor(const QString& xy, const QString& text) const
+{
+	qDebug() << __FUNCTION__;
+	mglGraph gr;
+	mglParse pr;
+	pr.AllowSetSize(true);
+	setlocale(LC_CTYPE, "");
+	setlocale(LC_NUMERIC, "C");
+	pr.Execute(&gr,text.toAscii());
+	gr.Finish();
+
+	int x = (int)xy.section(" ",0,0).toDouble();
+	int y = (int)xy.section(" ",1,1).toDouble();
+	mglPoint p = gr.CalcXYZ(x,y);
+	QString res;
+	res.sprintf("x = %g, y = %g, z = %g for point (%d, %d)\n", p.x, p.y, p.z, x,y);
+	qDebug() << res+"\nask"+xy;
+	return res+"\nask"+xy;
+}
+//-----------------------------------------------------------------------------
+QString Backend::geometry(const QString& mgl) const
+{
+	qDebug() << __FUNCTION__;
+	const char *tmp = tmpnam(0);
+	mglGraph gr;
+#if 0
+	gr.SetFaceNum(200);
+#endif
+	mglParse pr;
+	pr.AllowSetSize(true);
+	setlocale(LC_CTYPE, "");
+	setlocale(LC_NUMERIC, "C");
+	pr.Execute(&gr,mgl.toAscii());
+	gr.WriteJSON(tmp);
+	setlocale(LC_NUMERIC, "");
+
+	QFile f(tmp);
+	f.open(QIODevice::ReadOnly);
+	QTextStream ts(&f);
+	ts.setAutoDetectUnicode(true);
+	const QString json = ts.readAll();
+	f.remove();
+	return json;
+}
+//-----------------------------------------------------------------------------
diff --git a/json/Backend.hpp b/json/Backend.hpp
new file mode 100644
index 0000000..35b3207
--- /dev/null
+++ b/json/Backend.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <QtCore/QStringList>
+#include <QtCore/QObject>
+
+class Backend : public QObject
+{
+	Q_OBJECT
+
+public:
+	explicit Backend(QObject *parent = 0);
+
+public:
+//	Q_INVOKABLE QString join(const QStringList list) const;
+	Q_INVOKABLE QString show(const QString& text) const;
+	Q_INVOKABLE QString coor(const QString& xy, const QString& text) const;
+	Q_INVOKABLE QString geometry(const QString& mgl) const;
+};
+
diff --git a/json/CMakeLists.txt b/json/CMakeLists.txt
new file mode 100644
index 0000000..79d4716
--- /dev/null
+++ b/json/CMakeLists.txt
@@ -0,0 +1,14 @@
+if(MGL_HAVE_QT)
+
+set(json_src Backend.cpp MainWindow.cpp)
+set(json_moc_hdr Backend.hpp MainWindow.hpp)
+
+include(${QT_USE_FILE})
+include_directories(${MathGL_BINARY_DIR}/json)
+
+qt4_wrap_ui(json_ui_src MainWindow.ui)
+qt4_wrap_cpp(json_moc_src ${json_moc_hdr} )
+add_executable(MglForJsTestBench ${json_src} ${json_moc_src} ${json_ui_src})
+target_link_libraries(MglForJsTestBench mgl-qt ${QT_LIBRARIES})
+
+endif(MGL_HAVE_QT)
diff --git a/json/MainWindow.cpp b/json/MainWindow.cpp
new file mode 100644
index 0000000..30c5df1
--- /dev/null
+++ b/json/MainWindow.cpp
@@ -0,0 +1,53 @@
+#include "MainWindow.hpp"
+#include "ui_MainWindow.h"
+
+#include <QtWebKit/QWebFrame>
+#include <QtNetwork/QNetworkDiskCache>
+#include <QtGui/QDesktopServices>
+//-----------------------------------------------------------------------------
+int main(int argc, char *argv[])
+{
+	QApplication a(argc, argv);
+	MainWindow w;
+	w.show();
+	
+	return a.exec();
+}
+//-----------------------------------------------------------------------------
+MainWindow::MainWindow(QWidget* const parent) : QMainWindow(parent), ui(new Ui::MainWindow)
+{
+	ui->setupUi(this);
+
+	// configure webkit
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, true);
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
+	QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptCanAccessClipboard, true);
+
+	// create non-cached QNetworkAccessManager and assign to webview
+	QNetworkAccessManager* manager = new QNetworkAccessManager(this);
+	QNetworkDiskCache* diskCache = new QNetworkDiskCache();
+	const QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
+	diskCache->setCacheDirectory(location);
+	diskCache->setMaximumCacheSize(0);
+	manager->setCache(diskCache);
+	ui->webView->page()->setNetworkAccessManager(manager);
+
+	// inject backend object each time javascript object is cleared
+	connect(ui->webView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(injectBackendObject()));
+
+	// set url to load
+//	ui->webView->load(QUrl(QString("file:///%1/%2").arg(qApp->applicationDirPath()).arg("index.html")));
+	ui->webView->load(QUrl("index.html"));
+}
+//-----------------------------------------------------------------------------
+void MainWindow::injectBackendObject()
+{
+	ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("globalBackend", &_backend, QScriptEngine::QtOwnership);
+}
+//-----------------------------------------------------------------------------
+MainWindow::~MainWindow()	{	delete ui;	}
+//-----------------------------------------------------------------------------
diff --git a/json/MainWindow.hpp b/json/MainWindow.hpp
new file mode 100644
index 0000000..5b2f84e
--- /dev/null
+++ b/json/MainWindow.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Backend.hpp"
+#include <QtGui/QMainWindow>
+#include <QtCore/QObject>
+
+namespace Ui {	class MainWindow;	}
+
+class MainWindow : public QMainWindow {
+	Q_OBJECT
+
+public:
+	explicit MainWindow(QWidget *parent = 0);
+	~MainWindow();
+
+private slots:
+	void injectBackendObject();
+
+private:
+	Ui::MainWindow *ui;
+	Backend _backend;
+};
+
diff --git a/json/MainWindow.ui b/json/MainWindow.ui
new file mode 100644
index 0000000..e808739
--- /dev/null
+++ b/json/MainWindow.ui
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>850</width>
+    <height>650</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QWebView" name="webView">
+        <property name="url">
+         <url>
+          <string>about:blank</string>
+         </url>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+  <customwidget>
+   <class>QWebView</class>
+   <extends>QWidget</extends>
+   <header>QtWebKit/QWebView</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/json/RotationExample.py b/json/RotationExample.py
new file mode 100644
index 0000000..85e2a44
--- /dev/null
+++ b/json/RotationExample.py
@@ -0,0 +1,25 @@
+"""
+.. versionadded:: 1.1.0
+   This demo depends on new features added to contourf3d.
+"""
+
+from mpl_toolkits.mplot3d import axes3d
+import matplotlib.pyplot as plt
+
+fig = plt.figure()
+ax = fig.gca(projection='3d')
+X, Y, Z = axes3d.get_test_data(0.05)
+ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
+cset = ax.contourf(X, Y, Z, zdir='z', offset=-100)
+cset = ax.contourf(X, Y, Z, zdir='x', offset=-40)
+cset = ax.contourf(X, Y, Z, zdir='y', offset=40)
+
+ax.set_xlabel('X')
+ax.set_xlim(-40, 40)
+ax.set_ylabel('Y')
+ax.set_ylim(-40, 40)
+ax.set_zlabel('Z')
+ax.set_zlim(-100, 100)
+
+plt.show()
+
diff --git a/json/index.html b/json/index.html
new file mode 100644
index 0000000..43c0083
--- /dev/null
+++ b/json/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <script type="text/javascript" src="sylvester.js"></script>
+  <script type="text/javascript" src="mathgl.js"></script>
+  <script type="text/javascript" src="mathgl.View.js"></script>
+  <script type="text/javascript" src="mathgl.Backend.js"></script>
+  <script type="text/javascript" src="mathgl.WebkitBackend.js"></script>
+  <script type="text/javascript" src="mathgl.Graph.js"></script>
+  <script type="text/javascript" src="main.js"></script>
+</head>
+
+<body onload="main();">
+  <center>
+    <button type="button" onclick="graph.moveLeft()">Left</button>
+    <button type="button" onclick="graph.moveUp()">Up</button>
+    <button type="button" onclick="graph.zoomIn()">Zoom in</button>
+    <button type="button" onclick="graph.zoomOut()">Zoom out</button>
+    <button type="button" onclick="graph.moveDown()">Down</button>
+    <button type="button" onclick="graph.moveRight()">Right</button>
+    <canvas id="canvas" width="800" height="600" style="border:1px solid #d3d3d3;">
+    </canvas>
+  </center>
+</body>
+</html>
diff --git a/json/main.js b/json/main.js
new file mode 100644
index 0000000..e35326b
--- /dev/null
+++ b/json/main.js
@@ -0,0 +1,57 @@
+var graph;
+
+/**
+ * Entry function.
+ */
+var main = function() {
+  // get canvas to work over from the DOM
+  var canvas = document.getElementById("canvas");
+
+  // get backend injected by from C++ side just to illustrate where it came from
+  // in normal web application it shall be implemented using some transport protocol
+  var backend = new mathgl.WebkitBackend();
+
+  // instantiate mathgl graph instance connecting it with canvas and backend
+  graph = new mathgl.Graph(canvas, backend);
+  graph.setBackgroundFillStyle("#F0F0F0");
+  // initialize it by some sample MGL script
+  graph.init(makeSampleScript());
+}
+
+
+//var script = "rotate 40 60:fsurf 'sin(2*pi*x*y)'\nbox:axis:fplot 'sin(2*pi*t)' 'cos(2*pi*t)' '2*t-1' 'm2o'";
+//var script = "box:axis:fplot 'sin(pi*x)'";
+//var script = "rotate 10 20: box:axis:fsurf 'sin(pi*x*y)'";
+
+var makeSampleScript = function() {
+  var mgl = "";
+  mgl += "ranges -2 2 -2 2 -2 2:"   // NOTE: Ranges MUST BE specified for correctly work of zoomaxis feature
+  mgl += "facenum 50:";
+//  mgl += "rotate 1 0:";
+
+  //mgl += "origin 0 0 0:axis 'Uxyz':xlabel 'x':ylabel 'y':zlabel 'z':"
+  mgl += "origin 0 0 0:axis 'x':xlabel 'x':ylabel 'y':zlabel 'z':"
+
+  mgl += "box:"
+
+//mgl += "fsurf 'x':"
+  mgl += "fplot 'sin(x^2)'\n";   // This is just for testing zoomaxis features
+  mgl += "fplot 'sin(2*pi*t)' '2*t-1' 'cos(2*pi*t)' 'm2o':";
+//mgl += "fsurf '0':"
+//  mgl += "fplot 'sin(2*pi*t)' '2*t-1' '2*cos(2*pi*t)' 'm2o':";
+
+  //mgl += "axis:fsurf 'sin(2*pi*x*y)':";
+mgl += "axis:fsurf 'cos(2*pi*x*y)':";
+
+  //mgl += "zoomaxis";
+///////  mgl += "box:axis:fplot 'sin(2*pi*t)' 'cos(2*pi*t)' '2*t-1' 'm2o'\n";
+
+  //mgl += "xlabel 'x':ylabel 'y':axis:fsurf 'sin(2*pi*x*y)'\n";
+  //mgl += "box:axis:fplot 'sin(2*pi*t)' 'cos(2*pi*t)' '2*t-1' 'm2o'\n";
+  // only axes are kept for debug purposes
+
+  //  mgl += "rotate 1 0:xlabel 'x':ylabel 'y':axis:\n";
+
+//mgl += "fsurf 'sin(2*pi*x*y)':"
+  return mgl;  
+}
diff --git a/json/mathgl.Backend.js b/json/mathgl.Backend.js
new file mode 100644
index 0000000..6c1ec74
--- /dev/null
+++ b/json/mathgl.Backend.js
@@ -0,0 +1,13 @@
+/**
+ * Backend interface. The only responsibility its successors is to return geometry object by given MGL script.
+ */
+
+/** constructor */
+mathgl.Backend = function() {}
+
+/**
+ * Request for geometry data for given MGL script.
+ * @param mgl {String} MGL script containing all the information neede to build geometry
+ * @return {Object} geometry data gathered from server-side
+ */
+mathgl.Backend.prototype.geometry = function(mgl) { throw new Error("abstract method invoked"); }
diff --git a/json/mathgl.Graph.js b/json/mathgl.Graph.js
new file mode 100644
index 0000000..7cb17f9
--- /dev/null
+++ b/json/mathgl.Graph.js
@@ -0,0 +1,590 @@
+/**
+ * Graph - main MathGL class.
+ */
+
+/**
+ * Constructor - create graph attached to specified canvas and working over provided backend.
+ * @param backend {mathgl.Backend} implementation of backend interface
+ * @param canvas {Canvas} canvas to plot graph on
+ */
+mathgl.Graph = function(canvas, backend) {
+  this.__backend = backend;
+  this.__canvas = canvas;
+  this.__view = null;
+  this.__geometry = null;
+  // indicate whether rendering handlers are in the event queue
+  this.__isDraftRenderingInScheduled = false;
+  this.__isPreciseRenderingScheduled = false;
+  // draft rendering finished timestamp
+  this.__draftFinishedTimestamp = new Date();
+  this.__backgroundFillStyle = '#EEEEFF';
+  this.__preciseRenderingDelay = 700;
+  // TODO add setters/getters
+  this.__maxDraftPoints = 9000;
+  this.__x1 = 0;    this.__y1 = 0;
+  this.__x2 = 1;    this.__y2 = 1;
+}
+
+
+/**
+ * Initialize current view by given MGL script.
+ * @param mgl {String} MGL script
+ */
+mathgl.Graph.prototype.init = function(mgl) {
+  // request backend for geometry object
+  this.__geometry = this.__backend.geometry(mgl);
+  this.__geometry.mgl = mgl;
+  // construct view according the view type recieved from backend (within geometry object) and initialize it
+
+  this.__view = new mathgl.View();
+  // connect method which starts rendering to view object
+  this.__view.setRenderLauncher(mathgl.bind(this.__renderStart, this));
+  // connect pick point handler
+  this.__view.setPickPointHandler(mathgl.bind(this.__pickPointHandler, this));
+  // attach canvas to view
+  this.__view.attachCanvas(this.__canvas);
+}
+
+
+/**
+ * Load graph state from JSON string.
+ * @param json {String} string in JSON format with previously saved state
+ */
+mathgl.Graph.prototype.load = function(json) {
+  throw new Error("TODO");
+}
+
+
+/**
+ * Save current graph state to JSON string.
+ * @return {String} state serialized to JSON string
+ */
+mathgl.Graph.prototype.save = function() {
+  throw new Error("TODO");
+}
+
+
+/**
+ * background fill style setter
+ * @param fillStyle something that will be accepted by canvas' 2d context as fill style, e.g. color, gradient, pattern.
+ */
+mathgl.Graph.prototype.setBackgroundFillStyle = function(fillStyle) {
+  this.__backgroundFillStyle = fillStyle;
+}
+
+
+/** @return background fill style */
+mathgl.Graph.prototype.backgroundFillStyle = function() {
+  return this.__backgroundFillStyle;
+}
+
+
+/** called when user picks the point on the graph, point shall be somehow displayed/highlighted */
+mathgl.Graph.prototype.__pickPointHandler = function(x, y) {
+    console.log("Point picked: ", x, y);
+    var obj = this.__geometry;
+    var xy = x*obj.width/this.__canvas.width + " " + y*obj.height/this.__canvas.height;
+    // introduce zoom and view coomand for server side
+    var zoom = "zoom "+(0.5-obj.pf/2)+" "+(0.5-obj.pf/2)+" "+(0.5+obj.pf/2)+" "+(0.5+obj.pf/2)+"\n";
+    var view1 = "view 0 "+this.__view.__pitch*180/Math.PI+" 0"+"\n";
+    var view2 = "view 0 0 "+(-this.__view.__yaw)*180/Math.PI+"\n";
+    console.debug(xy,"pf=",obj.pf,zoom+view1+view2)
+    // now ask server side for proper coordinates
+    var res = globalBackend.coor(xy, zoom+view1+view2+obj.mgl);
+    console.debug("coordinates are ", res);
+}
+
+
+/** called when user shift axis range */
+mathgl.Graph.prototype.shiftAxis = function(x, y) {
+    var dx = x*(this.__x2-this.__x1), dy = y*(this.__y2-this.__y1)
+    this.__x1 += dx; this.__x2 += dx;
+    this.__y1 += dy; this.__y2 += dy;
+    // introduce zoomaxis coomand for server side
+    var zoom = "zoomaxis "+this.__x1+" "+this.__y1+" "+this.__x2+" "+this.__y2+"\n";
+    var mgl = this.__geometry.mgl;
+            console.log(zoom, "Old: ", this.__geometry);
+    // now ask server side for proper coordinates
+    this.__geometry = this.__backend.geometry(zoom+mgl);
+    this.__geometry.mgl = mgl;
+            console.log("New: ", this.__geometry);
+    this.__renderStart();
+}
+
+
+/** called when user shift axis range */
+mathgl.Graph.prototype.zoomAxis = function(factor) {
+    var d, c;
+    d=(this.__x2-this.__x1)*factor/2; c=(this.__x2+this.__x1)/2;
+    this.__x1 = c-d; this.__x2 = c+d;
+    d=(this.__y2-this.__y1)*factor/2; c=(this.__y2+this.__y1)/2;
+    this.__y1 = c-d; this.__y2 = c+d;
+    // introduce zoomaxis coomand for server side
+    var zoom = "zoomaxis "+this.__x1+" "+this.__y1+" "+this.__x2+" "+this.__y2+"\n";
+    var mgl = this.__geometry.mgl;
+            console.log(zoom, "Old: ", this.__geometry);
+    // now ask server side for proper coordinates
+    this.__geometry = this.__backend.geometry(zoom+this.__geometry.mgl);
+    this.__geometry.mgl = mgl;
+            console.log("New: ", this.__geometry);
+    this.__renderStart();
+}
+
+
+/** initiate the chains of rendering the geometry to the canvas */
+mathgl.Graph.prototype.__renderStart = function() {
+  // do nothing if processing is already started
+  if (!this.__isDraftRenderingInScheduled) {
+    // enqueue draft rendering step
+    this.__isDraftRenderingInScheduled = true;
+    setTimeout(mathgl.bind(this.__renderDraft, this), 0);
+  }
+}
+
+
+/** draft rendering */
+mathgl.Graph.prototype.__renderDraft = function() {
+  this.__drawMesh(false);
+  this.__isDraftRenderingInScheduled = false;
+
+  // enqueue precise rendering step
+  if (!this.__isPreciseRenderingScheduled) {
+    this.__isPreciseRenderingScheduled = true;
+    setTimeout(mathgl.bind(this.__renderPrecise, this), this.__preciseRenderingDelay);
+  }
+  this.__draftFinishedTimestamp = new Date();
+}
+
+
+/** precise rendering */
+mathgl.Graph.prototype.__renderPrecise = function() {
+  // do nothing if draft rendering is scheduled
+  if (this.__isDraftRenderingInScheduled) {
+    this.__isPreciseRenderingScheduled = false;
+    return;
+  }
+
+  // check that enough time has passed since last occurance of draft rendering finished
+  // rechedule pricese rendering if it is not
+  var current = new Date();
+  if (current - this.__draftFinishedTimestamp < this.__preciseRenderingDelay) {
+    setTimeout(mathgl.bind(this.__renderPrecise, this), this.__preciseRenderingDelay - (current - this.__draftFinishedTimestamp));
+    return;
+  }
+  this.__drawMesh(true);
+  this.__isPreciseRenderingScheduled = false;
+}
+
+
+/** fill canvas background */
+mathgl.Graph.prototype.__drawBackground = function() {
+  var c = this.__canvas.getContext("2d");
+  var h = this.__canvas.height;
+  var w = this.__canvas.width;
+  c.fillStyle = this.__backgroundFillStyle;
+  c.fillRect(0, 0 , w, h);
+}
+
+
+/** auxiliary function to draw mesh */
+mathgl.Graph.prototype.__drawMesh = function(isPrecise) {
+  var c = this.__canvas.getContext("2d");
+  var m = this.__view.viewMatrix().inverse();
+//  var vvv = $M([[1,0,0,1]]);
+//  console.log(vvv.x(m).elements);
+  var obj = this.__geometry;
+  var h = this.__canvas.height;
+  var dy = h / obj.height;
+  var w = this.__canvas.width;
+  var dx = w / obj.width;
+  obj.pf = -m.e(4,3);
+  obj.b = [dx*m.e(1,1),  dx*m.e(2,1),  dx*m.e(3,1),
+           dy*m.e(1,2),  dy*m.e(2,2),  dy*m.e(3,2),
+           m.e(1,3),     m.e(2,3),     m.e(3,3),
+           w/2, h/2, obj.depth/2];
+
+  this.__drawBackground();
+
+  if (!isPrecise) {
+    obj.fast = 1;
+    obj.good = 0;
+    this.__mgl_draw_fast(obj,c,1);
+  } else {
+    obj.fast = 0;
+    obj.good = 1;
+    this.__mgl_draw_good(obj,c,1);
+  }
+}
+
+
+/** perform fast drawing */
+mathgl.Graph.prototype.__mgl_draw_fast = function(obj, ctx, skip) {
+    if(obj.fast==0)	return;
+    this.__mgl_prepare(obj,skip);	// update coordinates
+    var i,n1,n2;
+    // for each primitive skipping superfluous
+    for(var i=0;i<obj.nprim;i += 1 + Math.round(obj.nprim / this.__maxDraftPoints))
+    {
+        n1 = obj.prim[i][1];    n2 = obj.prim[i][2];
+        if(obj.prim[i][0]==1)
+        {
+            ctx.strokeStyle = obj.prim[i][10];
+            ctx.beginPath();
+            ctx.moveTo(obj.pp[n1][0],obj.pp[n1][1]);
+            ctx.lineTo(obj.pp[n2][0],obj.pp[n2][1]);
+            ctx.lineWidth = obj.prim[i][7];
+            ctx.stroke();
+        }
+        else
+		{
+			ctx.fillStyle = obj.prim[i][10];
+			ctx.fillRect(obj.pp[n1][0], obj.pp[n1][1], 2, 2);
+		}
+    }
+}
+
+
+/** perform high-quality drawing */
+mathgl.Graph.prototype.__mgl_draw_good = function(obj, ctx, skip) {
+    obj.fast = 0;
+    this.__mgl_prepare(obj,skip);	// update coordinates
+//	var scl = 1/Math.abs(obj.z[1]-obj.z[0]);
+    // NOTE: this valid only for current zoom/view. In general case it should be more complicated
+    var s1 = Math.sqrt(obj.b[0]*obj.b[0]+obj.b[1]*obj.b[1]+obj.b[2]*obj.b[2]);
+    var deg = Math.PI/180;  //0.017453293;
+    for(var i=0;i<obj.nprim;i++)	// for each primitive
+    {
+        var scl = s1*this.__mgl_pf(obj, obj.prim[i][9]);
+        var n1 = obj.prim[i][1], n2 = obj.prim[i][2];
+        var n3 = obj.prim[i][3], n4 = obj.prim[i][4];
+        ctx.strokeStyle = obj.prim[i][10];
+        ctx.fillStyle = obj.prim[i][10];
+        ctx.lineWidth = 1;
+        switch(obj.prim[i][0])		// draw it depending on its type
+        {
+        case 0: // marks
+            ctx.lineWidth = obj.prim[i][7]*obj.prim[i][6]*50;
+            this.__mgl_draw_mark(ctx, obj.pp[n1][0], obj.pp[n1][1], n4, obj.prim[i][6], scl);
+            break;
+        case 1: // lines
+            ctx.beginPath();
+            ctx.moveTo(obj.pp[n1][0],obj.pp[n1][1]);
+            ctx.lineTo(obj.pp[n2][0],obj.pp[n2][1]);
+            ctx.lineWidth = obj.prim[i][7];
+            ctx.stroke();	break;
+        case 2: // triangles
+            ctx.beginPath();
+            ctx.moveTo(obj.pp[n1][0],obj.pp[n1][1]);
+            ctx.lineTo(obj.pp[n2][0],obj.pp[n2][1]);
+            ctx.lineTo(obj.pp[n3][0],obj.pp[n3][1]);
+            ctx.closePath();	ctx.fill();	break;
+        case 3: // quadrangles
+            ctx.beginPath();
+            ctx.moveTo(obj.pp[n1][0],obj.pp[n1][1]);
+            ctx.lineTo(obj.pp[n2][0],obj.pp[n2][1]);
+            ctx.lineTo(obj.pp[n4][0],obj.pp[n4][1]);
+            ctx.lineTo(obj.pp[n3][0],obj.pp[n3][1]);
+            ctx.closePath();
+            // NOTE: look as alpha is disabled for lines
+            // So, next code should be only for the case alpha=false
+            if(obj.prim[i][10].charAt(0)=='#')	ctx.stroke();
+            ctx.fill();	break;
+        case 4: // glyphs
+            var t=obj.prim[i][7]*deg;
+            var xx=obj.coor[n2][2],yy=-obj.coor[n2][3],zz=obj.coor[n2][4];
+            var xc = obj.b[0]*xx + obj.b[1]*yy + obj.b[2]*zz;
+            var yc = obj.b[3]*xx + obj.b[4]*yy + obj.b[5]*zz;
+            var zc = obj.b[6]*xx + obj.b[7]*yy + obj.b[8]*zz;
+            var ll = xc*xc+yc*yc;
+            if(ll < 1e-10)	break;
+            console.debug("t=",t);
+            if(ll<1e10 && t/deg<1e4)
+            {
+                t = Math.atan2(yc,xc);
+                if(Math.abs(t)>Math.PI/2)	t += Math.PI;
+            }
+            else t=0;
+            var c=Math.cos(t), s=Math.sin(t), d=obj.prim[i][6]/2;
+
+            var b=[d*c, d*s, d*s, -d*c, obj.pp[n1][0],obj.pp[n1][1]];
+            var x=obj.coor[n2][0]*scl, y=obj.coor[n2][1]*scl, f=obj.prim[i][8]*scl;
+            if(n3&8)
+            {
+                if(!(n3&4))	this.__mgl_line_glyph(ctx, x,y, f,1,b);
+                this.__mgl_line_glyph(ctx, x,y, f,0,b);
+            }
+            else
+            {
+                if(!(n3&4)) this.__mgl_fill_glyph(ctx, x,y, f,obj.glfs[n4],b);
+                this.__mgl_wire_glyph(ctx, x,y, f,obj.glfs[n4],b);
+            }
+            break;
+        }
+    }
+}
+
+
+mathgl.Graph.prototype.__mgl_pf = function(obj, z) {
+  return 1/obj.pf;
+// return 1/(1+obj.pf*(1-z/obj.depth));
+}
+
+
+/** change coordinates according current transformations, usually called internally by draw() */
+mathgl.Graph.prototype.__mgl_prepare = function(obj, skip) {
+    // fill transformation matrix
+    if(!skip)
+    {
+        var dx = 1/Math.abs(obj.z[1]-obj.z[0]);
+        var dy = 1/Math.abs(obj.z[3]-obj.z[2]);
+        var cx=Math.cos(obj.tet*deg), sx=Math.sin(obj.tet*deg);	// tetx
+        var cy=Math.cos(obj.phi*deg), sy=Math.sin(obj.phi*deg);	// tety
+        var cz=Math.cos(obj.bet*deg), sz=Math.sin(obj.bet*deg);	// tetz
+        obj.b = [dx*cx*cy, -dx*cy*sx, dx*sy,
+                    dy*(cx*sy*sz+cz*sx), dy*(cx*cz-sx*sy*sz), -dy*cy*sz,
+                    sx*sz-cx*cz*sy, cx*sz+cz*sx*sy, cy*cz,
+                    obj.width/2*(1+dx-obj.z[1]-obj.z[0])/dx,
+                    obj.height/2*(1+dy-obj.z[3]-obj.z[2])/dy, obj.depth/2];
+    }
+    // now transform points for found transformation matrix
+    var b = obj.b, i;
+    for(i=0;i<obj.npnts;i++)
+    {
+        var x = obj.pnts[i][0]-obj.width/2;
+        var y = obj.pnts[i][1]-obj.height/2;
+        var z = obj.pnts[i][2]-obj.depth/2;
+        obj.pp[i] = [b[9]  + b[0]*x + b[1]*y + b[2]*z,
+                     b[10] + b[3]*x + b[4]*y + b[5]*z,
+                     b[11] + b[6]*x + b[7]*y + b[8]*z];
+    }
+    if(obj.pf)	for(var i=0;i<obj.npnts;i++)	// perspective
+    {	// NOTE: it is not supported for coordinate determining now
+        var d = this.__mgl_pf(obj, obj.pp[i][2]);
+        obj.pp[i][0] = d*obj.pp[i][0] + (1-d)*obj.b[9];
+        obj.pp[i][1] = d*obj.pp[i][1] + (1-d)*obj.b[10];
+    }
+    // fill z-coordinates for primitives
+    if(!obj.fast)   for(i=0;i<obj.nprim;i++)
+    {
+        var n1 = obj.prim[i][1], n2 = obj.prim[i][2], n3 = obj.prim[i][3], n4 = obj.prim[i][4];
+        switch(obj.prim[i][0])
+        {
+        case 1: // lines
+            obj.prim[i][9] = (obj.pp[n1][2]+obj.pp[n2][2])/2;	break;
+        case 2: // triangles
+            obj.prim[i][9] = (obj.pp[n1][2]+obj.pp[n2][2]+obj.pp[n3][2])/3;	break;
+        case 3: // quadrangles
+            obj.prim[i][9] = (obj.pp[n1][2]+obj.pp[n2][2]+obj.pp[n3][2]+obj.pp[n4][2])/4;	break;
+        default:
+            obj.prim[i][9] = obj.pp[n1][2];	break;
+        }
+    }
+    if(!obj.fast) // sort primitives according its z-coordinate
+        obj.prim.sort(this.__mgl_cmp); // more accurate sorting
+}
+
+
+mathgl.Graph.prototype.__mgl_cmp = function(a,b) {
+    var tt = [0,2,4,5, 1,3,6, 7];
+    if(a[9]!=b[9])	return a[9] - b[9];
+    if(a[0]!=b[0])	return tt[b[0]]-tt[a[0]];
+    if(a[8]!=b[8])	return a[8] - b[8];
+    return a[3]-b[3];
+}
+
+
+/**
+ * Function for drawing markers of type st with given size at position {x,y}
+ * Usually this function is called internally, but it can be called by user as well
+ */
+mathgl.Graph.prototype.__mgl_draw_mark = function(ctx,x,y,st,size,d) {
+    if(size<=0) {	st = 46;	size=1; }
+    var s = size*d;
+    ctx.beginPath();
+    switch(st)
+    {
+    case 111:	// 'o'
+        ctx.arc(x,y,s,0,Math.PI*2);	 ctx.stroke();	break;
+    case 79:	// 'O'
+        ctx.arc(x,y,s,0,Math.PI*2);	 ctx.fill();	 break;
+    case 67:	// 'C'
+        ctx.arc(x,y,s,0,Math.PI*2);	 ctx.stroke();
+        ctx.arc(x,y,0.1*s,0,Math.PI*2); ctx.fill();	 break;
+    case 80:	// 'P'
+        ctx.moveTo(x-s,y-s);	ctx.lineTo(x+s,y-s);
+        ctx.lineTo(x+s,y+s);	ctx.lineTo(x-s,y+s);	ctx.lineTo(x-s,y-s);
+        ctx.moveTo(x-s,y);		ctx.lineTo(x+s,y);
+        ctx.moveTo(x,y-s);		ctx.lineTo(x,y+s);
+        ctx.stroke();	break;
+    case 43:	// '+'
+        ctx.moveTo(x-s,y);		ctx.lineTo(x+s,y);
+        ctx.moveTo(x,y-s);		ctx.lineTo(x,y+s);
+        ctx.stroke();	break;
+    case 88:	// 'X'
+        ctx.moveTo(x-s,y-s);	ctx.lineTo(x+s,y-s);
+        ctx.lineTo(x+s,y+s);	ctx.lineTo(x-s,y+s);	ctx.lineTo(x-s,y-s);
+        ctx.moveTo(x-s,y-s);	ctx.lineTo(x+s,y+s);
+        ctx.moveTo(x+s,y-s);	ctx.lineTo(x-s,y+s);
+        ctx.stroke();	break;
+    case 120:	// 'x'
+        ctx.moveTo(x-s,y-s);	ctx.lineTo(x+s,y+s);
+        ctx.moveTo(x+s,y-s);	ctx.lineTo(x-s,y+s);
+        ctx.stroke();	break;
+    case 115:	// 's'
+        ctx.moveTo(x-s,y-s);	ctx.lineTo(x+s,y-s);
+        ctx.lineTo(x+s,y+s);	ctx.lineTo(x-s,y+s);
+        ctx.closePath();		ctx.stroke();	break;
+    case 83:	// 'S'
+        ctx.moveTo(x-s,y-s);	ctx.lineTo(x+s,y-s);
+        ctx.lineTo(x+s,y+s);	ctx.lineTo(x-s,y+s);
+        ctx.closePath();		ctx.fill();	 break;
+    case 100:	// 'd'
+        ctx.moveTo(x-s,y);		ctx.lineTo(x,y-s);
+        ctx.lineTo(x+s,y);		ctx.lineTo(x,y+s);
+        ctx.closePath();		ctx.stroke();	break;
+    case 68:	// 'D'
+        ctx.moveTo(x-s,y);		ctx.lineTo(x,y-s);
+        ctx.lineTo(x+s,y);		ctx.lineTo(x,y+s);
+        ctx.closePath();		ctx.fill();	 break;
+    case 42:	// '*'
+        ctx.moveTo(x-s,y);		ctx.lineTo(x+s,y);
+        ctx.moveTo(x-0.6*s,y-0.8*s);	ctx.lineTo(x+0.6*s,y+0.8*s);
+        ctx.moveTo(x+0.6*s,y-0.8*s);	ctx.lineTo(x-0.6*s,y+0.8*s);
+        ctx.stroke();	break;
+    case 89:	// 'Y'
+        ctx.moveTo(x,y-s);		ctx.lineTo(x,y);
+        ctx.moveTo(x-0.8*s,y+0.6*s);	ctx.lineTo(x,y);
+        ctx.moveTo(x+0.8*s,y+0.6*s);	ctx.lineTo(x,y);
+        ctx.stroke();	break;
+    case 86:	// 'T'
+        ctx.moveTo(x-s,y-s/2);	ctx.lineTo(x+s,y-s/2);
+        ctx.lineTo(x,y+s);		ctx.closePath();
+        ctx.fill();	 break;
+    case 118:	// '^'
+        ctx.moveTo(x-s,y-s/2);	ctx.lineTo(x+s,y-s/2);
+        ctx.lineTo(x,y+s);		ctx.closePath();
+        ctx.stroke();	break;
+    case 84:	// 'V'
+        ctx.moveTo(x-s,y+s/2);	ctx.lineTo(x+s,y+s/2);
+        ctx.lineTo(x,y-s);		ctx.closePath();
+        ctx.fill();	 break;
+    case 94:	// 'v'
+        ctx.moveTo(x-s,y+s/2);	ctx.lineTo(x+s,y+s/2);
+        ctx.lineTo(x,y-s);		ctx.closePath();
+        ctx.stroke();	break;
+    case 76:	// 'L'
+        ctx.moveTo(x+s/2,y-s);	ctx.lineTo(x+s/2,y+s);
+        ctx.lineTo(x-s,y);		ctx.closePath();
+        ctx.fill();	 break;
+    case 60:	// '<'
+        ctx.moveTo(x+s/2,y-s);	ctx.lineTo(x+s/2,y+s);
+        ctx.lineTo(x-s,y);		ctx.closePath();
+        ctx.stroke();	break;
+    case 82:	// 'R'
+        ctx.moveTo(x-s/2,y-s);	ctx.lineTo(x-s/2,y+s);
+        ctx.lineTo(x+s,y);		ctx.closePath();
+        ctx.fill();	 break;
+    case 62:	// '>'
+        ctx.moveTo(x-s/2,y-s);	ctx.lineTo(x-s/2,y+s);
+        ctx.lineTo(x+s,y);		ctx.closePath();
+        ctx.stroke();	break;
+//	case 46:	// '.'
+    default:
+        ctx.rect(x,y,1,1); ctx.fill();	 break;
+    }
+}
+
+
+/** for internal use only */
+mathgl.Graph.prototype.__mgl_fill_glyph = function(ctx, x,y, f,g,b) {
+    var xx,yy,j,xs,ys;
+    for(j=0;j<g[0];j++)
+    {
+        xx = x+f*g[2][6*j];	yy = y+f*g[2][6*j+1]; ctx.beginPath();
+        ctx.moveTo(b[4]+b[0]*xx+b[1]*yy, b[5]+b[2]*xx+b[3]*yy)
+        xx = x+f*g[2][6*j+2]; yy = y+f*g[2][6*j+3];
+        ctx.lineTo(b[4]+b[0]*xx+b[1]*yy, b[5]+b[2]*xx+b[3]*yy)
+        xx = x+f*g[2][6*j+4]; yy = y+f*g[2][6*j+5];
+        ctx.lineTo(b[4]+b[0]*xx+b[1]*yy, b[5]+b[2]*xx+b[3]*yy)
+        ctx.closePath();	ctx.fill();
+    }
+}
+
+
+/** for internal use only */
+mathgl.Graph.prototype.__mgl_wire_glyph = function(ctx, x,y, f,g,b) {
+    var xx,yy,j,xs,ys;
+    var np=1;
+    for(j=0;j<g[1];j++)
+    {
+        xx = g[3][2*j]; yy = g[3][2*j+1];	ctx.beginPath();
+        if(xx==16383 && yy==16383)
+        {
+            ctx.closePath();	ctx.stroke();
+            ctx.beginPath();	np = 1;
+        }
+        else if(np)
+        {
+            xx = x+f*xx;	yy = y+f*yy;	np = 0;
+            ctx.moveTo(b[4]+b[0]*xx+b[1]*yy, b[5]+b[2]*xx+b[3]*yy);
+        }
+        else
+        {
+            xx = x+f*xx;	yy = y+f*yy;
+            ctx.lineTo(b[4]+b[0]*xx+b[1]*yy, b[5]+b[2]*xx+b[3]*yy);
+        }
+        ctx.closePath();	ctx.stroke();
+    }
+}
+
+
+/** for internal use only */
+mathgl.Graph.prototype.__mgl_line_glyph = function(ctx, x,y, f,solid,b) {
+    var xx,yy,j,xs,ys;
+    var dy = 0.004;
+    ctx.moveTo(b[4]+b[0]*x+b[1]*(y-dy), b[5]+b[2]*x+b[3]*(y-dy));
+    ctx.lineTo(b[4]+b[0]*x+b[1]*(y+dy), b[5]+b[2]*x+b[3]*(y+dy));
+    ctx.lineTo(b[4]+b[0]*(x+f)+b[1]*(y+dy), b[5]+b[2]*(x+f)+b[3]*(y+dy));
+    ctx.lineTo(b[4]+b[0]*(x+f)+b[1]*(y-dy), b[5]+b[2]*(x+f)+b[3]*(y-dy));
+    ctx.closePath();
+    if(solid)	ctx.fill();
+    else		ctx.stroke();
+}
+
+
+mathgl.Graph.prototype.moveLeft = function() { 
+	var b = this.__geometry.b;
+	var f = 0.1/Math.sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);
+	this.shiftAxis(f*b[0],f*b[1]);
+	//this.shiftAxis(-0.1,0);
+}
+
+mathgl.Graph.prototype.moveRight = function() { 
+	var b = this.__geometry.b;
+	var f = -0.1/Math.sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);
+	this.shiftAxis(f*b[0],f*b[1]);
+	//this.shiftAxis(0.1,0);
+}
+
+mathgl.Graph.prototype.moveUp = function() { 
+	var b = this.__geometry.b;
+	var f = -0.1/Math.sqrt(b[3]*b[3]+b[4]*b[4]+b[5]*b[5]);
+	this.shiftAxis(f*b[3],f*b[4]);
+	//this.shiftAxis(0,-0.1);
+}
+
+mathgl.Graph.prototype.moveDown = function() { 
+	var b = this.__geometry.b;
+	var f = 0.1/Math.sqrt(b[3]*b[3]+b[4]*b[4]+b[5]*b[5]);
+	this.shiftAxis(f*b[3],f*b[4]);
+	//this.shiftAxis(0,0.1);
+}
+
+mathgl.Graph.prototype.zoomIn = function() { 
+  this.zoomAxis(1.1);
+}
+
+mathgl.Graph.prototype.zoomOut = function() { 
+  this.zoomAxis(1./1.1);
+}
+
diff --git a/json/mathgl.View.js b/json/mathgl.View.js
new file mode 100644
index 0000000..b00c5c7
--- /dev/null
+++ b/json/mathgl.View.js
@@ -0,0 +1,187 @@
+/**
+ * Standard 3D view - camera flying along the surface of a sphere.
+ */
+
+
+/** @constructor */
+mathgl.View = function() {
+  this.__canvas = null;
+  this.__renderLauncherFunc = null;
+  this.__pickPointHandlerFunc = null;
+  this.__onMouseMoveFunc = mathgl.bind(this.__onMouseMove, this);
+  this.__onMouseDownFunc = mathgl.bind(this.__onMouseDown, this);
+  this.__onMouseUpFunc = mathgl.bind(this.__onMouseUp, this);
+  this.__onMouseOutFunc = mathgl.bind(this.__onMouseOut, this);
+  this.__onDblClickFunc = mathgl.bind(this.__onDblClick, this);
+  this.__onMouseWheelFunc = mathgl.bind(this.__onMouseWheel, this);
+
+  // mouse state
+  this.__isMouseDown = false;
+  this.__mouseX = -1;
+  this.__mouseY = -1;
+
+  // current camera position
+  this.__distance = 1.0;
+  this.__pitch = 0;
+  this.__yaw = 0;
+}
+
+
+/**
+ * Load view state from JSON string.
+ * @param json {String} string in JSON format with previously saved state
+ */
+mathgl.View.prototype.load = function(json) {
+  throw new Error("not implemented");
+}
+
+
+/**
+ * Save view state to JSON string.
+ * @return {String} view state serialized to JSON string
+ */
+mathgl.View.prototype.save = function() {
+  throw new Error("not implemented");
+}
+
+
+/**
+ * Attach given canvas to this view (Bind current view with given canvas). View instance will start procesing
+ * canvas events since it is attached to it.
+ * @param canvas {Canvas} canvas to attach
+ */
+mathgl.View.prototype.attachCanvas = function(canvas) {
+  // remember canvas
+  this.__canvas = canvas;
+  // connect mouse events
+  this.__canvas.addEventListener("mousemove", this.__onMouseMoveFunc, false);
+  this.__canvas.addEventListener("mousedown", this.__onMouseDownFunc, false);
+  this.__canvas.addEventListener("mouseup",   this.__onMouseUpFunc, false);
+  this.__canvas.addEventListener("mouseout",  this.__onMouseOutFunc, false);
+  this.__canvas.addEventListener("dblclick",  this.__onDblClickFunc, false);
+  this.__canvas.addEventListener("mousewheel",  this.__onMouseWheelFunc, false);
+
+  // initiare redraw
+  this.__renderLauncherFunc();
+}
+
+
+/** Detach any previously attached canvas from this view instance. */
+mathgl.View.prototype.detachCanvas = function() {
+  // disconnect mouse events
+  this.__canvas.removeEventListener("mousemove", this.__onMouseMoveFunc, false);
+  this.__canvas.removeEventListener("mousedown", this.__onMouseDownFunc, false);
+  this.__canvas.removeEventListener("mouseup", this.__onMouseUpFunc, false);
+  this.__canvas.removeEventListener("mouseout", this.__onMouseOutFunc, false);
+  this.__canvas.removeEventListener("dblclick", this.__onDblClickFunc, false);
+  this.__canvas.removeEventListener("mousewheel",  this.__onMouseWheelFunc, false);
+
+  // drop canvas
+  this.__canvas = null;
+}
+
+
+/**
+ * Set render launcher - callback function which is invoked when graph shall be redrawn (after some user actions).
+ * @param renderLauncherFunc {Function} callback function which will be invoked when graph shall be redrawn
+ */
+mathgl.View.prototype.setRenderLauncher = function(renderLauncherFunc) {
+  this.__renderLauncherFunc = renderLauncherFunc;
+}
+
+
+/**
+ * Set pick point handler - callback function which is invoked when user request to highlight or show coordinates of point picked.
+ * @param pickPointHandler {Function} callback function which will be invoked when user pick the point
+ */
+mathgl.View.prototype.setPickPointHandler = function(pickPointHandlerFunc) {
+  this.__pickPointHandlerFunc = pickPointHandlerFunc;
+}
+
+
+/**
+ * Get current view view matrix.
+ * @return {Matrix} view matrix
+ */
+mathgl.View.prototype.viewMatrix = function() {
+  var d = this.__distance;
+  var cp = Math.cos(this.__pitch);
+  var sp = Math.sin(this.__pitch);
+  var cy = Math.cos(this.__yaw);
+  var sy = Math.sin(this.__yaw);
+  var lh = true; // coordinate system is left handed
+
+  var distanceMatrix = $M([[1, 0,  0, 0],
+                           [0, 1,  0, 0],
+                           [0, 0,  1, 0],
+                           [0, 0, d, 1]]);
+  var pitchMatrix = $M([[1,              0,              0, 0],
+                        [0,  cp           ,  lh ? sp : -sp, 0],
+                        [0,  lh ? -sp : sp,             cp, 0],
+                        [0,              0,              0, 1]]);
+  var yawMatrix = $M([[ cy, 0, -sy, 0],
+                      [  0, 1,   0, 0],
+                      [ sy, 0,  cy, 0],
+                      [  0, 0,   0, 1]]);
+  var viewMatrix = Matrix.I(4);
+  viewMatrix = viewMatrix.x(distanceMatrix);
+  viewMatrix = viewMatrix.x(pitchMatrix);
+  viewMatrix = viewMatrix.x(yawMatrix);
+  return viewMatrix;
+}
+
+/** TODO: add function returning current view area in world (model) coordinates */
+mathgl.View.prototype.viewArea = function() {
+  throw new Error("not implemented");
+}
+
+
+mathgl.View.prototype.__onMouseMove = function(e) {
+  if (this.__isMouseDown) {
+    var x = e.offsetX;
+    var y = e.offsetY;
+    this.__yaw += 0.5 * (this.__mouseX - x) * Math.PI / 180;
+    this.__pitch += 0.5 * (y - this.__mouseY) * Math.PI / 180;
+    this.__mouseX = x;
+    this.__mouseY = y
+    this.__pitch = Math.min(this.__pitch, Math.PI / 2);
+    this.__pitch = Math.max(this.__pitch, -Math.PI / 2);
+    this.__yaw = Math.min(this.__yaw, Math.PI);
+    this.__yaw = Math.max(this.__yaw, -Math.PI);
+    this.__renderLauncherFunc();
+  }
+}
+
+
+mathgl.View.prototype.__onMouseDown = function(e) {
+  this.__mouseX = e.offsetX;
+  this.__mouseY = e.offsetY;
+  this.__isMouseDown = true;
+}
+
+
+mathgl.View.prototype.__onMouseUp = function() {
+  this.__isMouseDown = false;
+}
+
+
+mathgl.View.prototype.__onMouseOut = function() {
+  this.__isMouseDown = false;
+}
+
+
+mathgl.View.prototype.__onDblClick = function(e) {
+  this.__pickPointHandlerFunc(e.offsetX, e.offsetY);
+}
+
+
+mathgl.View.prototype.__onMouseWheel = function(e) {
+  this.__isMouseDown = false;
+  this.__distance -= 0.1 * e.wheelDelta / 120;
+  this.__distance = Math.min(this.__distance, 10.0);
+  this.__distance = Math.max(this.__distance, 0.5);
+  this.__renderLauncherFunc();
+}
+
+
+
diff --git a/json/mathgl.WebkitBackend.js b/json/mathgl.WebkitBackend.js
new file mode 100644
index 0000000..92e4c8c
--- /dev/null
+++ b/json/mathgl.WebkitBackend.js
@@ -0,0 +1,31 @@
+/**
+ * Webkit backend. Backend base on the object injected by QWebView.
+ */
+
+/** constructor */
+mathgl.WebkitBackend = function() {}
+
+/** inherit from mathgl.Backend interface */
+mathgl.WebkitBackend.prototype = new mathgl.Backend();
+
+
+/** return geometry */
+mathgl.WebkitBackend.prototype.geometry = function(mgl) {
+    var geometryData = globalBackend.geometry(mgl);
+    console.log("geometryData size:",geometryData.length);
+
+	/*
+	var zlib = require('zlib');
+	zlib.unzip(geometryData, function(err, buffer) {
+		if (!err)
+		{	geometryData = buffer;
+			console.log("geometryData size:",geometryData.length);	}
+	 });
+	 */
+	
+    var obj = JSON.parse(geometryData);
+    obj.pp = new Array();
+    for(var i=0;i<obj.npnts;i++)	// copy original data for transformation
+        obj.pp[i] = [obj.pnts[i][0],obj.pnts[i][1],obj.pnts[i][2]];
+    return obj;
+}
diff --git a/json/mathgl.js b/json/mathgl.js
new file mode 100644
index 0000000..02fe4a4
--- /dev/null
+++ b/json/mathgl.js
@@ -0,0 +1,29 @@
+/** main mathgl namespace */
+var mathgl = {
+  version: '0.1.0'
+}
+
+
+/**
+ * Auxiliary functions.
+ */
+
+/** trivial bind implementation */
+mathgl.bind = function(func, context) {
+  return function() {
+    func.apply(context, arguments);
+  };
+}
+
+
+/** clone */
+mathgl.clone = function(obj) {
+  if (obj === null || typeof(obj) != 'object') {
+    return obj;
+  }
+  var temp = new obj.constructor();
+  for (var key in obj) {
+    temp[key] = mathgl.clone(obj[key]);
+  }
+  return temp;
+}
diff --git a/json/sylvester.js b/json/sylvester.js
new file mode 100644
index 0000000..3e83bee
--- /dev/null
+++ b/json/sylvester.js
@@ -0,0 +1 @@
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9 17={3i:\'0.1.3\',16:1e-6};l v(){}v.23={e:l(i){8(i<1||i>7.4.q)?w:7.4[i-1]},2R:l(){8 7.4.q},1u:l(){8 F.1x(7.2u(7))},24:l(a){9 n=7.4.q;9 V=a.4||a;o(n!=V.q){8 1L}J{o(F.13(7.4[n-1]-V[n-1])>17.16){8 1L}}H(--n);8 2x},1q:l(){8 v.u(7.4)},1b:l(a){9 b=[];7.28(l(x,i){b.19(a(x,i))});8 v.u(b)},28:l(a){9 n=7.4.q,k=n,i;J{i=k-n;a(7.4[i],i+1)}H(--n)},2q:l(){9 r=7.1u();o(r===0){8 7.1q()}8 7.1b(l(x){8 x/r})},1C:l(a){9 V=a.4||a;9 n=7.4.q,k=n,i;o(n!=V.q){8 w}9 b=0,1D=0,1F=0;7.28(l(x,i){b+=x*V[i-1];1D+=x*x;1F+=V[i-1]*V[i-1]});1D=F.1x(1D);1F=F.1x(1F);o(1D*1F===0){8 w}9 c=b/(1D*1F);o(c<-1){c=-1}o(c>1){c=1}8 F.37(c)},1m:l(a){9 b=7.1C(a);8(b===w)?w:(b<=17.16)},34:l(a){9 b=7.1C(a);8(b===w)?w:(F.13(b-F.1A)<=17.16)},2k:l(a){9 b=7.2u(a);8(b===w)?w:(F.13(b)<=17.16)},2j:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x+V[i-1]})},2C:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x-V[i-1]})},22:l(k){8 7.1b(l(x){8 x*k})},x:l(k){8 7.22(k)},2u:l(a){9 V=a.4||a;9 i,2g=0,n=7.4.q;o(n!=V.q){8 w}J{2g+=7.4[n-1]*V[n-1]}H(--n);8 2g},2f:l(a){9 B=a.4||a;o(7.4.q!=3||B.q!=3){8 w}9 A=7.4;8 v.u([(A[1]*B[2])-(A[2]*B[1]),(A[2]*B[0])-(A[0]*B[2]),(A[0]*B[1])-(A[1]*B[0])])},2A:l(){9 m=0,n=7.4.q,k=n,i;J{i=k-n;o(F.13(7.4[i])>F.13(m)){m=7.4[i]}}H(--n);8 m},2Z:l(x){9 a=w,n=7.4.q,k=n,i;J{i=k-n;o(a===w&&7.4[i]==x){a=i+1}}H(--n);8 a},3g:l(){8 S.2X(7.4)},2d:l(){8 7.1b(l(x){8 F.2d(x)})},2V:l(x){8 7.1b(l(y){8(F.13(y-x)<=17.16)?x:y})},1o:l(a){o(a.K){8 a.1o(7)}9 V=a.4||a;o(V.q!=7.4.q){8 w}9 b=0,2b;7.28(l(x,i){2b=x-V[i-1];b+=2b*2b});8 F.1x(b)},3a:l(a){8 a.1h(7)},2T:l(a){8 a.1h(7)},1V:l(t,a){9 V,R,x,y,z;2S(7.4.q){27 2:V=a.4||a;o(V.q!=2){8 w}R=S.1R(t).4;x=7.4[0]-V[0];y=7.4[1]-V[1];8 v.u([V[0]+R[0][0]*x+R[0][1]*y,V[1]+R[1][0]*x+R[1][1]*y]);1I;27 3:o(!a.U){8 w}9 C=a.1r(7).4;R=S.1R(t,a.U).4;x=7.4[0]-C[0];y=7.4[1]-C[1];z=7.4[2]-C[2];8 v.u([C[0]+R[0][0]*x+R[0][1]*y+R[0][2]*z,C[1]+R[1][0]*x+R[1][1]*y+R[1][2]*z,C[2]+R[2][0]*x+R[2][1]*y+R[2][2]*z]);1I;2P:8 w}},1t:l(a){o(a.K){9 P=7.4.2O();9 C=a.1r(P).4;8 v.u([C[0]+(C[0]-P[0]),C[1]+(C[1]-P[1]),C[2]+(C[2]-(P[2]||0))])}1d{9 Q=a.4||a;o(7.4.q!=Q.q){8 w}8 7.1b(l(x,i){8 Q[i-1]+(Q[i-1]-x)})}},1N:l(){9 V=7.1q();2S(V.4.q){27 3:1I;27 2:V.4.19(0);1I;2P:8 w}8 V},2n:l(){8\'[\'+7.4.2K(\', \')+\']\'},26:l(a){7.4=(a.4||a).2O();8 7}};v.u=l(a){9 V=25 v();8 V.26(a)};v.i=v.u([1,0,0]);v.j=v.u([0,1,0]);v.k=v.u([0,0,1]);v.2J=l(n){9 a=[];J{a.19(F.2F())}H(--n);8 v.u(a)};v.1j=l(n){9 a=[];J{a.19(0)}H(--n);8 v.u(a)};l S(){}S.23={e:l(i,j){o(i<1||i>7.4.q||j<1||j>7.4[0].q){8 w}8 7.4[i-1][j-1]},33:l(i){o(i>7.4.q){8 w}8 v.u(7.4[i-1])},2E:l(j){o(j>7.4[0].q){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][j-1])}H(--n);8 v.u(a)},2R:l(){8{2D:7.4.q,1p:7.4[0].q}},2D:l(){8 7.4.q},1p:l(){8 7.4[0].q},24:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(7.4.q!=M.q||7.4[0].q!=M[0].q){8 1L}9 b=7.4.q,15=b,i,G,10=7.4[0].q,j;J{i=15-b;G=10;J{j=10-G;o(F.13(7.4[i][j]-M[i][j])>17.16){8 1L}}H(--G)}H(--b);8 2x},1q:l(){8 S.u(7.4)},1b:l(a){9 b=[],12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;b[i]=[];J{j=10-G;b[i][j]=a(7.4[i][j],i+1,j+1)}H(--G)}H(--12);8 S.u(b)},2i:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4.q==M.q&&7.4[0].q==M[0].q)},2j:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x+M[i-1][j-1]})},2C:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x-M[i-1][j-1]})},2B:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4[0].q==M.q)},22:l(a){o(!a.4){8 7.1b(l(x){8 x*a})}9 b=a.1u?2x:1L;9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2B(M)){8 w}9 d=7.4.q,15=d,i,G,10=M[0].q,j;9 e=7.4[0].q,4=[],21,20,c;J{i=15-d;4[i]=[];G=10;J{j=10-G;21=0;20=e;J{c=e-20;21+=7.4[i][c]*M[c][j]}H(--20);4[i][j]=21}H(--G)}H(--d);9 M=S.u(4);8 b?M.2E(1):M},x:l(a){8 7.22(a)},32:l(a,b,c,d){9 e=[],12=c,i,G,j;9 f=7.4.q,1p=7.4[0].q;J{i=c-12;e[i]=[];G=d;J{j=d-G;e[i][j]=7.4[(a+i-1)%f][(b+j-1)%1p]}H(--G)}H(--12);8 S.u(e)},31:l(){9 a=7.4.q,1p=7.4[0].q;9 b=[],12=1p,i,G,j;J{i=1p-12;b[i]=[];G=a;J{j=a-G;b[i][j]=7.4[j][i]}H(--G)}H(--12);8 S.u(b)},1y:l(){8(7.4.q==7.4[0].q)},2A:l(){9 m=0,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(F.13(7.4[i][j])>F.13(m)){m=7.4[i][j]}}H(--G)}H(--12);8 m},2Z:l(x){9 a=w,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(7.4[i][j]==x){8{i:i+1,j:j+1}}}H(--G)}H(--12);8 w},30:l(){o(!7.1y){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][i])}H(--n);8 v.u(a)},1K:l(){9 M=7.1q(),1c;9 n=7.4.q,k=n,i,1s,1n=7.4[0].q,p;J{i=k-n;o(M.4[i][i]==0){2e(j=i+1;j<k;j++){o(M.4[j][i]!=0){1c=[];1s=1n;J{p=1n-1s;1c.19(M.4[i][p]+M.4[j][p])}H(--1s);M.4[i]=1c;1I}}}o(M.4[i][i]!=0){2e(j=i+1;j<k;j++){9 a=M.4[j][i]/M.4[i][i];1c=[];1s=1n;J{p=1n-1s;1c.19(p<=i?0:M.4[j][p]-M.4[i][p]*a)}H(--1s);M.4[j]=1c}}}H(--n);8 M},3h:l(){8 7.1K()},2z:l(){o(!7.1y()){8 w}9 M=7.1K();9 a=M.4[0][0],n=M.4.q-1,k=n,i;J{i=k-n+1;a=a*M.4[i][i]}H(--n);8 a},3f:l(){8 7.2z()},2y:l(){8(7.1y()&&7.2z()===0)},2Y:l(){o(!7.1y()){8 w}9 a=7.4[0][0],n=7.4.q-1,k=n,i;J{i=k-n+1;a+=7.4[i][i]}H(--n);8 a},3e:l(){8 7.2Y()},1Y:l(){9 M=7.1K(),1Y=0;9 a=7.4.q,15=a,i,G,10=7.4[0].q,j;J{i=15-a;G=10;J{j=10-G;o(F.13(M.4[i][j])>17.16){1Y++;1I}}H(--G)}H(--a);8 1Y},3d:l(){8 7.1Y()},2W:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}9 T=7.1q(),1p=T.4[0].q;9 b=T.4.q,15=b,i,G,10=M[0].q,j;o(b!=M.q){8 w}J{i=15-b;G=10;J{j=10-G;T.4[i][1p+j]=M[i][j]}H(--G)}H(--b);8 T},2w:l(){o(!7.1y()||7.2y()){8 w}9 a=7.4.q,15=a,i,j;9 M=7.2W(S.I(a)).1K();9 b,1n=M.4[0].q,p,1c,2v;9 c=[],2c;J{i=a-1;1c=[];b=1n;c[i]=[];2v=M.4[i][i];J{p=1n-b;2c=M.4[i][p]/2v;1c.19(2c);o(p>=15){c[i].19(2c)}}H(--b);M.4[i]=1c;2e(j=0;j<i;j++){1c=[];b=1n;J{p=1n-b;1c.19(M.4[j][p]-M.4[i][p]*M.4[j][i])}H(--b);M.4[j]=1c}}H(--a);8 S.u(c)},3c:l(){8 7.2w()},2d:l(){8 7.1b(l(x){8 F.2d(x)})},2V:l(x){8 7.1b(l(p){8(F.13(p-x)<=17.16)?x:p})},2n:l(){9 a=[];9 n=7.4.q,k=n,i;J{i=k-n;a.19(v.u(7.4[i]).2n())}H(--n);8 a.2K(\'\\n\')},26:l(a){9 i,4=a.4||a;o(1g(4[0][0])!=\'1f\'){9 b=4.q,15=b,G,10,j;7.4=[];J{i=15-b;G=4[i].q;10=G;7.4[i]=[];J{j=10-G;7.4[i][j]=4[i][j]}H(--G)}H(--b);8 7}9 n=4.q,k=n;7.4=[];J{i=k-n;7.4.19([4[i]])}H(--n);8 7}};S.u=l(a){9 M=25 S();8 M.26(a)};S.I=l(n){9 a=[],k=n,i,G,j;J{i=k-n;a[i]=[];G=k;J{j=k-G;a[i][j]=(i==j)?1:0}H(--G)}H(--n);8 S.u(a)};S.2X=l(a){9 n=a.q,k=n,i;9 M=S.I(n);J{i=k-n;M.4[i][i]=a[i]}H(--n);8 M};S.1R=l(b,a){o(!a){8 S.u([[F.1H(b),-F.1G(b)],[F.1G(b),F.1H(b)]])}9 d=a.1q();o(d.4.q!=3){8 w}9 e=d.1u();9 x=d.4[0]/e,y=d.4[1]/e,z=d.4[2]/e;9 s=F.1G(b),c=F.1H(b),t=1-c;8 S.u([[t*x*x+c,t*x*y-s*z,t*x*z+s*y],[t*x*y+s*z,t*y*y+c,t*y*z-s*x],[t*x*z-s*y,t*y*z+s*x,t*z*z+c]])};S.3b=l(t){9 c=F.1H(t),s=F.1G(t);8 S.u([[1,0,0],[0,c,-s],[0,s,c]])};S.39=l(t){9 c=F.1H(t),s=F.1G(t);8 S.u([[c,0,s],[0,1,0],[-s,0,c]])};S.38=l(t){9 c=F.1H(t),s=F.1G(t);8 S.u([[c,-s,0],[s,c,0],[0,0,1]])};S.2J=l(n,m){8 S.1j(n,m).1b(l(){8 F.2F()})};S.1j=l(n,m){9 a=[],12=n,i,G,j;J{i=n-12;a[i]=[];G=m;J{j=m-G;a[i][j]=0}H(--G)}H(--12);8 S.u(a)};l 14(){}14.23={24:l(a){8(7.1m(a)&&7.1h(a.K))},1q:l(){8 14.u(7.K,7.U)},2U:l(a){9 V=a.4||a;8 14.u([7.K.4[0]+V[0],7.K.4[1]+V[1],7.K.4[2]+(V[2]||0)],7.U)},1m:l(a){o(a.W){8 a.1m(7)}9 b=7.U.1C(a.U);8(F.13(b)<=17.16||F.13(b-F.1A)<=17.16)},1o:l(a){o(a.W){8 a.1o(7)}o(a.U){o(7.1m(a)){8 7.1o(a.K)}9 N=7.U.2f(a.U).2q().4;9 A=7.K.4,B=a.K.4;8 F.13((A[0]-B[0])*N[0]+(A[1]-B[1])*N[1]+(A[2]-B[2])*N[2])}1d{9 P=a.4||a;9 A=7.K.4,D=7.U.4;9 b=P[0]-A[0],2a=P[1]-A[1],29=(P[2]||0)-A[2];9 c=F.1x(b*b+2a*2a+29*29);o(c===0)8 0;9 d=(b*D[0]+2a*D[1]+29*D[2])/c;9 e=1-d*d;8 F.13(c*F.1x(e<0?0:e))}},1h:l(a){9 b=7.1o(a);8(b!==w&&b<=17.16)},2T:l(a){8 a.1h(7)},1v:l(a){o(a.W){8 a.1v(7)}8(!7.1m(a)&&7.1o(a)<=17.16)},1U:l(a){o(a.W){8 a.1U(7)}o(!7.1v(a)){8 w}9 P=7.K.4,X=7.U.4,Q=a.K.4,Y=a.U.4;9 b=X[0],1z=X[1],1B=X[2],1T=Y[0],1S=Y[1],1M=Y[2];9 c=P[0]-Q[0],2s=P[1]-Q[1],2r=P[2]-Q[2];9 d=-b*c-1z*2s-1B*2r;9 e=1T*c+1S*2s+1M*2r;9 f=b*b+1z*1z+1B*1B;9 g=1T*1T+1S*1S+1M*1M;9 h=b*1T+1z*1S+1B*1M;9 k=(d*g/f+h*e)/(g-h*h);8 v.u([P[0]+k*b,P[1]+k*1z,P[2]+k*1B])},1r:l(a){o(a.U){o(7.1v(a)){8 7.1U(a)}o(7.1m(a)){8 w}9 D=7.U.4,E=a.U.4;9 b=D[0],1l=D[1],1k=D[2],1P=E[0],1O=E[1],1Q=E[2];9 x=(1k*1P-b*1Q),y=(b*1O-1l*1P),z=(1l*1Q-1k*1O);9 N=v.u([x*1Q-y*1O,y*1P-z*1Q,z*1O-x*1P]);9 P=11.u(a.K,N);8 P.1U(7)}1d{9 P=a.4||a;o(7.1h(P)){8 v.u(P)}9 A=7.K.4,D=7.U.4;9 b=D[0],1l=D[1],1k=D[2],1w=A[0],18=A[1],1a=A[2];9 x=b*(P[1]-18)-1l*(P[0]-1w),y=1l*((P[2]||0)-1a)-1k*(P[1]-18),z=1k*(P[0]-1w)-b*((P[2]||0)-1a);9 V=v.u([1l*x-1k*z,1k*y-b*x,b*z-1l*y]);9 k=7.1o(P)/V.1u();8 v.u([P[0]+V.4[0]*k,P[1]+V.4[1]*k,(P[2]||0)+V.4[2]*k])}},1V:l(t,a){o(1g(a.U)==\'1f\'){a=14.u(a.1N(),v.k)}9 R=S.1R(t,a.U).4;9 C=a.1r(7.K).4;9 A=7.K.4,D=7.U.4;9 b=C[0],1E=C[1],1J=C[2],1w=A[0],18=A[1],1a=A[2];9 x=1w-b,y=18-1E,z=1a-1J;8 14.u([b+R[0][0]*x+R[0][1]*y+R[0][2]*z,1E+R[1][0]*x+R[1][1]*y+R[1][2]*z,1J+R[2][0]*x+R[2][1]*y+R[2][2]*z],[R[0][0]*D[0]+R[0][1]*D[1]+R[0][2]*D[2],R[1][0]*D[0]+R[1][1]*D[1]+R[1][2]*D[2],R[2][0]*D[0]+R[2][1]*D[1]+R[2][2]*D[2]])},1t:l(a){o(a.W){9 A=7.K.4,D=7.U.4;9 b=A[0],18=A[1],1a=A[2],2N=D[0],1l=D[1],1k=D[2];9 c=7.K.1t(a).4;9 d=b+2N,2h=18+1l,2o=1a+1k;9 Q=a.1r([d,2h,2o]).4;9 e=[Q[0]+(Q[0]-d)-c[0],Q[1]+(Q[1]-2h)-c[1],Q[2]+(Q[2]-2o)-c[2]];8 14.u(c,e)}1d o(a.U){8 7.1V(F.1A,a)}1d{9 P=a.4||a;8 14.u(7.K.1t([P[0],P[1],(P[2]||0)]),7.U)}},1Z:l(a,b){a=v.u(a);b=v.u(b);o(a.4.q==2){a.4.19(0)}o(b.4.q==2){b.4.19(0)}o(a.4.q>3||b.4.q>3){8 w}9 c=b.1u();o(c===0){8 w}7.K=a;7.U=v.u([b.4[0]/c,b.4[1]/c,b.4[2]/c]);8 7}};14.u=l(a,b){9 L=25 14();8 L.1Z(a,b)};14.X=14.u(v.1j(3),v.i);14.Y=14.u(v.1j(3),v.j);14.Z=14.u(v.1j(3),v.k);l 11(){}11.23={24:l(a){8(7.1h(a.K)&&7.1m(a))},1q:l(){8 11.u(7.K,7.W)},2U:l(a){9 V=a.4||a;8 11.u([7.K.4[0]+V[0],7.K.4[1]+V[1],7.K.4[2]+(V[2]||0)],7.W)},1m:l(a){9 b;o(a.W){b=7.W.1C(a.W);8(F.13(b)<=17.16||F.13(F.1A-b)<=17.16)}1d o(a.U){8 7.W.2k(a.U)}8 w},2k:l(a){9 b=7.W.1C(a.W);8(F.13(F.1A/2-b)<=17.16)},1o:l(a){o(7.1v(a)||7.1h(a)){8 0}o(a.K){9 A=7.K.4,B=a.K.4,N=7.W.4;8 F.13((A[0]-B[0])*N[0]+(A[1]-B[1])*N[1]+(A[2]-B[2])*N[2])}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;8 F.13((A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2])}},1h:l(a){o(a.W){8 w}o(a.U){8(7.1h(a.K)&&7.1h(a.K.2j(a.U)))}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=F.13(N[0]*(A[0]-P[0])+N[1]*(A[1]-P[1])+N[2]*(A[2]-(P[2]||0)));8(b<=17.16)}},1v:l(a){o(1g(a.U)==\'1f\'&&1g(a.W)==\'1f\'){8 w}8!7.1m(a)},1U:l(a){o(!7.1v(a)){8 w}o(a.U){9 A=a.K.4,D=a.U.4,P=7.K.4,N=7.W.4;9 b=(N[0]*(P[0]-A[0])+N[1]*(P[1]-A[1])+N[2]*(P[2]-A[2]))/(N[0]*D[0]+N[1]*D[1]+N[2]*D[2]);8 v.u([A[0]+D[0]*b,A[1]+D[1]*b,A[2]+D[2]*b])}1d o(a.W){9 c=7.W.2f(a.W).2q();9 N=7.W.4,A=7.K.4,O=a.W.4,B=a.K.4;9 d=S.1j(2,2),i=0;H(d.2y()){i++;d=S.u([[N[i%3],N[(i+1)%3]],[O[i%3],O[(i+1)%3]]])}9 e=d.2w().4;9 x=N[0]*A[0]+N[1]*A[1]+N[2]*A[2];9 y=O[0]*B[0]+O[1]*B[1]+O[2]*B[2];9 f=[e[0][0]*x+e[0][1]*y,e[1][0]*x+e[1][1]*y];9 g=[];2e(9 j=1;j<=3;j++){g.19((i==j)?0:f[(j+(5-i)%3)%3])}8 14.u(g,c)}},1r:l(a){9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=(A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2];8 v.u([P[0]+N[0]*b,P[1]+N[1]*b,(P[2]||0)+N[2]*b])},1V:l(t,a){9 R=S.1R(t,a.U).4;9 C=a.1r(7.K).4;9 A=7.K.4,N=7.W.4;9 b=C[0],1E=C[1],1J=C[2],1w=A[0],18=A[1],1a=A[2];9 x=1w-b,y=18-1E,z=1a-1J;8 11.u([b+R[0][0]*x+R[0][1]*y+R[0][2]*z,1E+R[1][0]*x+R[1][1]*y+R[1][2]*z,1J+R[2][0]*x+R[2][1]*y+R[2][2]*z],[R[0][0]*N[0]+R[0][1]*N[1]+R[0][2]*N[2],R[1][0]*N[0]+R[1][1]*N[1]+R[1][2]*N[2],R[2][0]*N[0]+R[2][1]*N[1]+R[2][2]*N[2]])},1t:l(a){o(a.W){9 A=7.K.4,N=7.W.4;9 b=A[0],18=A[1],1a=A[2],2M=N[0],2L=N[1],2Q=N[2];9 c=7.K.1t(a).4;9 d=b+2M,2p=18+2L,2m=1a+2Q;9 Q=a.1r([d,2p,2m]).4;9 e=[Q[0]+(Q[0]-d)-c[0],Q[1]+(Q[1]-2p)-c[1],Q[2]+(Q[2]-2m)-c[2]];8 11.u(c,e)}1d o(a.U){8 7.1V(F.1A,a)}1d{9 P=a.4||a;8 11.u(7.K.1t([P[0],P[1],(P[2]||0)]),7.W)}},1Z:l(a,b,c){a=v.u(a);a=a.1N();o(a===w){8 w}b=v.u(b);b=b.1N();o(b===w){8 w}o(1g(c)==\'1f\'){c=w}1d{c=v.u(c);c=c.1N();o(c===w){8 w}}9 d=a.4[0],18=a.4[1],1a=a.4[2];9 e=b.4[0],1W=b.4[1],1X=b.4[2];9 f,1i;o(c!==w){9 g=c.4[0],2l=c.4[1],2t=c.4[2];f=v.u([(1W-18)*(2t-1a)-(1X-1a)*(2l-18),(1X-1a)*(g-d)-(e-d)*(2t-1a),(e-d)*(2l-18)-(1W-18)*(g-d)]);1i=f.1u();o(1i===0){8 w}f=v.u([f.4[0]/1i,f.4[1]/1i,f.4[2]/1i])}1d{1i=F.1x(e*e+1W*1W+1X*1X);o(1i===0){8 w}f=v.u([b.4[0]/1i,b.4[1]/1i,b.4[2]/1i])}7.K=a;7.W=f;8 7}};11.u=l(a,b,c){9 P=25 11();8 P.1Z(a,b,c)};11.2I=11.u(v.1j(3),v.k);11.2H=11.u(v.1j(3),v.i);11.2G=11.u(v.1j(3),v.j);11.36=11.2I;11.35=11.2H;11.3j=11.2G;9 $V=v.u;9 $M=S.u;9 $L=14.u;9 $P=11.u;',62,206,'||||elements|||this|return|var||||||||||||function|||if||length||||create|Vector|null|||||||||Math|nj|while||do|anchor||||||||Matrix||direction||normal||||kj|Plane|ni|abs|Line|ki|precision|Sylvester|A2|push|A3|map|els|else||undefined|typeof|contains|mod|Zero|D3|D2|isParallelTo|kp|distanceFrom|cols|dup|pointClosestTo|np|reflectionIn|modulus|intersects|A1|sqrt|isSquare|X2|PI|X3|angleFrom|mod1|C2|mod2|sin|cos|break|C3|toRightTriangular|false|Y3|to3D|E2|E1|E3|Rotation|Y2|Y1|intersectionWith|rotate|v12|v13|rank|setVectors|nc|sum|multiply|prototype|eql|new|setElements|case|each|PA3|PA2|part|new_element|round|for|cross|product|AD2|isSameSizeAs|add|isPerpendicularTo|v22|AN3|inspect|AD3|AN2|toUnitVector|PsubQ3|PsubQ2|v23|dot|divisor|inverse|true|isSingular|determinant|max|canMultiplyFromLeft|subtract|rows|col|random|ZX|YZ|XY|Random|join|N2|N1|D1|slice|default|N3|dimensions|switch|liesIn|translate|snapTo|augment|Diagonal|trace|indexOf|diagonal|transpose|minor|row|isAntiparallelTo|ZY|YX|acos|RotationZ|RotationY|liesOn|RotationX|inv|rk|tr|det|toDiagonalMatrix|toUpperTriangular|version|XZ'.split('|'),0,{}))
\ No newline at end of file
diff --git a/mathgl-2x.cbp b/mathgl-2x.cbp
new file mode 100644
index 0000000..a958f50
--- /dev/null
+++ b/mathgl-2x.cbp
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="mathgl-2x" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Release">
+				<Option output="bin/Release/mathgl-2x" prefix_auto="1" extension_auto="1" />
+				<Option working_dir="bin/" />
+				<Option object_output="obj/Release/" />
+				<Option type="1" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-I include/" />
+				</Compiler>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add option="-march=core2" />
+			<Add option="-O3" />
+			<Add option="-O2" />
+			<Add option="-Wall" />
+			<Add option="-pg" />
+			<Add option="-g" />
+			<Add option="-fexceptions" />
+		</Compiler>
+		<Linker>
+			<Add option="-pg" />
+			<Add option="-pg -lgsl -lgslcblas" />
+			<Add option="-lpng -ljpeg -lgif -lhpdf" />
+			<Add option="-lhdf5 -ldf -lmfhdf" />
+		</Linker>
+		<Unit filename="build/include/mgl2/config.h" />
+		<Unit filename="examples/full_test.cpp" />
+		<Unit filename="examples/wnd_samples.cpp" />
+		<Unit filename="include/mgl2/addon.h" />
+		<Unit filename="include/mgl2/base.h" />
+		<Unit filename="include/mgl2/base_cf.h" />
+		<Unit filename="include/mgl2/canvas.h" />
+		<Unit filename="include/mgl2/canvas_cf.h" />
+		<Unit filename="include/mgl2/canvas_wnd.h" />
+		<Unit filename="include/mgl2/cont.h" />
+		<Unit filename="include/mgl2/data.h" />
+		<Unit filename="include/mgl2/data_cf.h" />
+		<Unit filename="include/mgl2/define.h" />
+		<Unit filename="include/mgl2/eval.h" />
+		<Unit filename="include/mgl2/evalc.h" />
+		<Unit filename="include/mgl2/fit.h" />
+		<Unit filename="include/mgl2/fltk.h" />
+		<Unit filename="include/mgl2/font.h" />
+		<Unit filename="include/mgl2/glut.h" />
+		<Unit filename="include/mgl2/mgl.h" />
+		<Unit filename="include/mgl2/mgl_cf.h" />
+		<Unit filename="include/mgl2/opengl.h" />
+		<Unit filename="include/mgl2/other.h" />
+		<Unit filename="include/mgl2/parser.h" />
+		<Unit filename="include/mgl2/plot.h" />
+		<Unit filename="include/mgl2/prim.h" />
+		<Unit filename="include/mgl2/qt.h" />
+		<Unit filename="include/mgl2/surf.h" />
+		<Unit filename="include/mgl2/type.h" />
+		<Unit filename="include/mgl2/vect.h" />
+		<Unit filename="include/mgl2/volume.h" />
+		<Unit filename="include/mgl2/window.h" />
+		<Unit filename="src/addon.cpp" />
+		<Unit filename="src/axis.cpp" />
+		<Unit filename="src/base.cpp" />
+		<Unit filename="src/base_cf.cpp" />
+		<Unit filename="src/canvas.cpp" />
+		<Unit filename="src/canvas_cf.cpp" />
+		<Unit filename="src/cont.cpp" />
+		<Unit filename="src/crust.cpp" />
+		<Unit filename="src/data.cpp" />
+		<Unit filename="src/data_io.cpp" />
+		<Unit filename="src/data_new.cpp" />
+		<Unit filename="src/data_png.cpp" />
+		<Unit filename="src/def_font.cpp" />
+		<Unit filename="src/eval.cpp" />
+		<Unit filename="src/evalc.cpp" />
+		<Unit filename="src/evalp.cpp" />
+		<Unit filename="src/exec.cpp" />
+		<Unit filename="src/export.cpp" />
+		<Unit filename="src/export_2d.cpp" />
+		<Unit filename="src/export_3d.cpp" />
+		<Unit filename="src/fit.cpp" />
+		<Unit filename="src/font.cpp" />
+		<Unit filename="src/other.cpp" />
+		<Unit filename="src/parser.cpp" />
+		<Unit filename="src/pde.cpp" />
+		<Unit filename="src/pixel.cpp" />
+		<Unit filename="src/plot.cpp" />
+		<Unit filename="src/prc.cpp" />
+		<Unit filename="src/prc/PRC.h" />
+		<Unit filename="src/prc/PRCbitStream.cc" />
+		<Unit filename="src/prc/PRCbitStream.h" />
+		<Unit filename="src/prc/PRCdouble.cc" />
+		<Unit filename="src/prc/PRCdouble.h" />
+		<Unit filename="src/prc/oPRCFile.cc" />
+		<Unit filename="src/prc/oPRCFile.h" />
+		<Unit filename="src/prc/writePRC.cc" />
+		<Unit filename="src/prc/writePRC.h" />
+		<Unit filename="src/prim.cpp" />
+		<Unit filename="src/s_hull/s_hull_pro.cpp" />
+		<Unit filename="src/s_hull/s_hull_pro.h" />
+		<Unit filename="src/surf.cpp" />
+		<Unit filename="src/tex_table.cpp" />
+		<Unit filename="src/vect.cpp" />
+		<Unit filename="src/volume.cpp" />
+		<Extensions>
+			<code_completion />
+			<envvars />
+			<debugger />
+			<lib_finder disable_auto="1" />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>
diff --git a/mgllab/CMakeLists.txt b/mgllab/CMakeLists.txt
new file mode 100644
index 0000000..f64ad22
--- /dev/null
+++ b/mgllab/CMakeLists.txt
@@ -0,0 +1,10 @@
+if(MGL_HAVE_FLTK)
+	include_directories(${FLTK_INCLUDE_DIR})
+	set(mgl_lab_src animate.cpp editor.cpp help.cpp mathgl.cpp setup.cpp write.cpp data.cpp grid.cpp main.cpp option.cpp table.cpp)
+	add_executable(mgllab ${mgl_lab_src})
+	target_link_libraries(mgllab mgl mgl-fltk ${FLTK_LIBRARIES})
+	install(
+		TARGETS mgllab
+		RUNTIME DESTINATION bin	)
+endif(MGL_HAVE_FLTK)
+
diff --git a/mgllab/animate.cpp b/mgllab/animate.cpp
new file mode 100644
index 0000000..3f08276
--- /dev/null
+++ b/mgllab/animate.cpp
@@ -0,0 +1,314 @@
+/* animate.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <ctype.h>
+#include <string.h>
+#include <FL/Fl_Tabs.H>
+#include <FL/Fl_Round_Button.H>
+#include <FL/Fl_Multiline_Input.H>
+#include <FL/Fl_Float_Input.H>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+struct ArgumentDlg
+{
+public:
+	Fl_Window* wnd;
+	int OK;
+	Fl_Input *a[10];
+
+	ArgumentDlg()	{	memset(this,0,sizeof(ArgumentDlg));	create_dlg();	}
+	~ArgumentDlg()	{	delete wnd;	}
+	void FillResult();
+protected:
+	void create_dlg();
+} argument_dlg;
+//-----------------------------------------------------------------------------
+void argument_dlg_cb(Fl_Widget *, void *v)
+{	argument_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void ArgumentDlg::create_dlg()
+{
+	wnd = new Fl_Window(325, 275, gettext("Script arguments"));
+	a[1] = new Fl_Input(10, 25, 150, 25, gettext("Value for $1"));	a[1]->align(FL_ALIGN_TOP_LEFT);
+	a[2] = new Fl_Input(165, 25, 150, 25, gettext("Value for $2"));	a[2]->align(FL_ALIGN_TOP_LEFT);
+	a[3] = new Fl_Input(10, 70, 150, 25, gettext("Value for $3"));	a[3]->align(FL_ALIGN_TOP_LEFT);
+	a[4] = new Fl_Input(165, 70, 150, 25, gettext("Value for $4"));	a[4]->align(FL_ALIGN_TOP_LEFT);
+	a[5] = new Fl_Input(10, 115, 150, 25, gettext("Value for $5"));	a[5]->align(FL_ALIGN_TOP_LEFT);
+	a[6] = new Fl_Input(165, 115, 150, 25, gettext("Value for $6"));a[6]->align(FL_ALIGN_TOP_LEFT);
+	a[7] = new Fl_Input(10, 160, 150, 25, gettext("Value for $7"));	a[7]->align(FL_ALIGN_TOP_LEFT);
+	a[8] = new Fl_Input(165, 160, 150, 25, gettext("Value for $8"));a[8]->align(FL_ALIGN_TOP_LEFT);
+	a[9] = new Fl_Input(10, 205, 150, 25, gettext("Value for $9"));	a[9]->align(FL_ALIGN_TOP_LEFT);
+	a[0] = new Fl_Input(165, 205, 150, 25, gettext("Value for $0"));a[0]->align(FL_ALIGN_TOP_LEFT);
+
+	Fl_Button *o;
+	o = new Fl_Button(75, 240, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(175, 240, 75, 25, gettext("OK"));	o->callback(argument_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void ArgumentDlg::FillResult()
+{
+	if(a[0]->value()[0])	Parse->AddParam(0,a[0]->value());
+	if(a[1]->value()[0])	Parse->AddParam(1,a[1]->value());
+	if(a[2]->value()[0])	Parse->AddParam(2,a[2]->value());
+	if(a[3]->value()[0])	Parse->AddParam(3,a[3]->value());
+	if(a[4]->value()[0])	Parse->AddParam(4,a[4]->value());
+	if(a[5]->value()[0])	Parse->AddParam(5,a[5]->value());
+	if(a[6]->value()[0])	Parse->AddParam(6,a[6]->value());
+	if(a[7]->value()[0])	Parse->AddParam(7,a[7]->value());
+	if(a[8]->value()[0])	Parse->AddParam(8,a[8]->value());
+	if(a[9]->value()[0])	Parse->AddParam(9,a[9]->value());
+}
+//-----------------------------------------------------------------------------
+void argument_cb(Fl_Widget *, void *)
+{
+	ArgumentDlg *s = &argument_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	s->FillResult();
+}
+//-----------------------------------------------------------------------------
+void argument_set(int n, const char *s)
+{
+	if(n<0 || n>9)	return;
+	Parse->AddParam(n,s);
+	argument_dlg.a[n]->value(s);
+}
+//-----------------------------------------------------------------------------
+AnimateDlg animate_dlg;
+//-----------------------------------------------------------------------------
+void animate_dlg_cb(Fl_Widget *, void *v)
+{
+	animate_dlg.swap = false;
+	if(!animate_dlg.rt->value() && !animate_dlg.rv->value())
+		fl_message(gettext("You have to select textual string or numeric cycle"));
+	else if(animate_dlg.rv->value() && animate_dlg.dx->value()==0)
+		fl_message(gettext("You have to set nonzero step in cycle"));
+	else
+	{
+		double t0=atof(animate_dlg.x0->value()), t1=atof(animate_dlg.x1->value()), dt=atof(animate_dlg.dx->value());
+		if((t1-t0)*dt<0)
+		{
+			if(fl_ask(gettext("Order of first and last value is wrong. Swap it?")))
+			{
+				char s[32];	snprintf(s,32,"%g",t0);
+				animate_dlg.x0->value(animate_dlg.x1->value());
+				animate_dlg.x1->value(s);
+			}
+			else
+			{	fl_message(gettext("Wrong boundaries"));	return;	}
+		}
+		animate_dlg.OK = true;	((Fl_Window *)v)->hide();
+	}
+}
+//-----------------------------------------------------------------------------
+void animate_rad_cb(Fl_Widget *, void *v)
+{
+	animate_dlg.rt->value(0);
+	animate_dlg.rv->value(0);
+	((Fl_Round_Button *)v)->value(1);
+}
+//-----------------------------------------------------------------------------
+void animate_put_cb(Fl_Widget *, void *)
+{
+	if(animate_dlg.rt->value())
+	{
+		if(animate_dlg.txt->value()==0 || strlen(animate_dlg.txt->value())==0)	return;
+		char *s = new char[1+strlen(animate_dlg.txt->value())], *a=s;
+		strcpy(s, animate_dlg.txt->value());
+		for(int i=0;s[i]!=0;i++)
+		{
+			if(s[i]=='\n')
+			{
+				s[i] = 0;
+				textbuf->append("\n##a ");
+				textbuf->append(a);
+				a = s+i+1;
+			}
+		}
+		if(*a)
+		{	textbuf->append("\n##a ");	textbuf->append(a);	}
+		delete []s;
+	}
+	else if(animate_dlg.rv->value())
+	{
+		char *s = new char[128];
+		snprintf(s,128,"\n##c %s %s %s",animate_dlg.x0->value(),animate_dlg.x1->value(),animate_dlg.dx->value());
+		textbuf->append(s);
+		delete []s;
+	}
+}
+//-----------------------------------------------------------------------------
+void AnimateDlg::create_dlg()
+{
+	wnd = new Fl_Window(335, 350, gettext("Animation"));
+	new Fl_Box(10, 5, 315, 25, gettext("Redraw picture for $0 equal to:"));
+	rt = new Fl_Round_Button(10, 30, 200, 25, gettext("strings in lines below"));
+	rt->callback(animate_rad_cb, rt);
+	rv = new Fl_Round_Button(220, 30, 105, 25, gettext("values"));
+	rv->callback(animate_rad_cb, rv);
+	txt = new Fl_Multiline_Input(10, 60, 200, 250);
+	x0 = new Fl_Float_Input(220, 80, 105, 25, gettext("from"));			x0->align(FL_ALIGN_TOP_LEFT);
+	x1 = new Fl_Float_Input(220, 130, 105, 25, gettext("to"));			x1->align(FL_ALIGN_TOP_LEFT);
+	dx = new Fl_Float_Input(220, 180, 105, 25, gettext("with step"));	dx->align(FL_ALIGN_TOP_LEFT);
+
+	Fl_Button *o;
+	o = new Fl_Button(230, 215, 80, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(230, 250, 80, 25, gettext("OK"));	o->callback(animate_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	save = new Fl_Check_Button(220, 285, 105, 25, gettext("save slides"));
+	save->tooltip(gettext("Keep slides in memory (faster animation but require more memory)"));
+	save->down_box(FL_DOWN_BOX);	save->hide();
+
+	o = new Fl_Button(10, 315, 100, 25, gettext("Put to script"));	o->callback(animate_put_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	dt = new Fl_Float_Input(220, 315, 105, 25, gettext("Delay (in sec)"));//	dx->align(FL_ALIGN_TOP_LEFT);
+
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void AnimateDlg::FillResult(Fl_MGL* e)
+{
+	e->NArgs = e->ArgCur = 0;
+	if(e->ArgBuf)	delete [](e->ArgBuf);	e->ArgBuf = 0;
+	e->AnimDelay = atof(dt->value());
+	if(rt->value())
+	{
+		char *s;
+		e->ArgBuf = new char[1+strlen(txt->value())];
+		strncpy(e->ArgBuf, txt->value(),32);
+		s = e->Args[0] = e->ArgBuf;	e->NArgs = 1;
+		for(int i=0;s[i]!=0;i++)
+			if(s[i]=='\n')
+			{
+				s[i] = 0;	e->Args[e->NArgs] = s+i+1;	e->NArgs += 1;
+			}
+		if(e->Args[e->NArgs-1][0]==0)	e->NArgs -= 1;
+	}
+	else if(rv->value() && atof(dx->value()))
+	{
+		double t0=atof(x0->value()), t1=atof(x1->value()), dt=atof(dx->value()), t;
+		if((t1-t0)/dt<1)
+		{
+			e->ArgBuf = new char[32];	snprintf(e->ArgBuf,32,"%g",t0);
+			e->NArgs = 1;	e->Args[0] = e->ArgBuf;	return;
+		}
+		if((t1-t0)/dt>999)
+		{
+			fl_message(gettext("Too many slides. Reduce to 1000 slides."));
+			dt = (t1-t0)/998;
+		}
+		e->ArgBuf = new char[32*int(1+(t1-t0)/dt)];
+		for(t=t0;(dt>0&&t<=t1)||(dt<0&&t>=t1);t+=dt)
+		{
+			snprintf(e->ArgBuf + 32*e->NArgs,32,"%g\0",t);
+			e->Args[e->NArgs] = e->ArgBuf + 32*e->NArgs;
+			e->NArgs += 1;
+		}
+	}
+	else	fl_message(gettext("No selection. So nothing to do"));
+}
+//-----------------------------------------------------------------------------
+void animate_cb(Fl_Widget *, void *v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	AnimateDlg *s = &animate_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	s->FillResult(e->graph);
+}
+//-----------------------------------------------------------------------------
+void cpy_arg_buf(const char *str, long *size, char **buf)
+{
+	const char *end;
+	for(end=str; *end>' '; end++);
+	if(end>=str+*size)
+	{	*size = end-str+1;	*buf = (char *)realloc(*buf,*size);	}
+	memset(*buf,0,*size);
+	strncpy(*buf,str,end-str);
+}
+//-----------------------------------------------------------------------------
+void fill_animate(const char *text)
+{
+	long size=128,i;
+	const char *str = text;
+	char *buf = (char *)malloc(size), tmp[4]="#$0";
+	for(i=0;i<10;i++)	// first read script arguments (if one)
+	{
+		tmp[2] = '0'+i;
+		if((str=strstr(text,tmp)))
+		{
+			str+=3;
+			while(*str>0 && *str<=' ' && *str!='\n')	str++;
+			cpy_arg_buf(str,&size,&buf);
+			argument_dlg.a[i]->value(buf);
+			Parse->AddParam(i,buf);
+		}
+	}
+
+	char *a = (char *)malloc(size);
+	memset(a,0,size);	i = 0;
+	str = text;
+	while((str = strstr(str, "##")))	// now read animation parameters
+	{
+		if(str[2]=='a')
+		{
+			str += 3;
+			while(*str>0 && *str<=' ' && *str!='\n')	str++;
+			if(*str==0 || *str=='\n')	return;	// empty comment
+			cpy_arg_buf(str,&size,&buf);
+			if(i==0)	Parse->AddParam(0,buf);	// put first value as $0
+			i += strlen(buf)+1;
+			if(i>=size)
+			{
+				size = (1+ (i+2)/128)*128;
+				a = (char *)realloc(a,size);
+			}
+			strcat(a,buf);	strcat(a,"\n");
+		}
+		if(str[2]=='c')
+		{
+			str += 3;
+			register long j=0,l=strlen(str);
+			char *s=new char[l+1],*s1=0,*s2=0,*s3=0;
+			bool sp=true;	strcpy(s,str);
+			for(j=0;j<l;j++)
+			{
+				if(isspace(s[j]))	{	s[j]=0;	sp=true;	}
+				else if(sp)
+				{
+					sp=false;
+					if(!s1)	s1=s+j;	else if(!s2) s2=s+j;	else s3=s+j;
+				}
+				animate_dlg.x0->value(s1);
+				animate_dlg.x1->value(s2);
+				animate_dlg.dx->value(s3);
+				animate_dlg.rv->value(1);
+			}
+			delete []s;
+		}
+	}
+	if(i)
+	{	animate_dlg.txt->value(a);	animate_dlg.rt->value(1);	}
+	free(buf);	free(a);
+}
diff --git a/mgllab/data.cpp b/mgllab/data.cpp
new file mode 100644
index 0000000..796f533
--- /dev/null
+++ b/mgllab/data.cpp
@@ -0,0 +1,353 @@
+/* data.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <FL/Fl_Choice.H>
+#include <FL/Fl_Spinner.H>
+#include <FL/Fl_Output.H>
+#include <FL/Fl_Check_Button.H>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+void option_in_cb(Fl_Widget *, void *v);
+void style_in_cb(Fl_Widget *, void *v);
+//-----------------------------------------------------------------------------
+struct VarDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Choice *var;
+	Fl_Spinner *dim1, *dim2, *dim3;
+	VarDlg()	{	memset(this,0,sizeof(VarDlg));	create_dlg();	}
+	~VarDlg()	{	delete wnd;	}
+	void create_dlg();
+	char *get_result();
+	void init();
+} var_dlg;
+//-----------------------------------------------------------------------------
+void VarDlg::init()
+{
+	char ss[1024];
+	var->clear();
+	mglVar *v=Parse->FindVar("");
+	while(v)
+	{
+		wcstombs(ss,v->s.c_str(),1024);
+		var->add(ss,0,0,v);
+		v = v->next;
+	}
+}
+//-----------------------------------------------------------------------------
+char *VarDlg::get_result()
+{
+	static char res[64];
+	char a1[16]=":",a2[16]=":",a3[16]=":";
+	res[0]=0;
+	if(var->value()<0)	return res;
+	const Fl_Menu_Item m=var->menu()[var->value()];
+	if(m.text[0])
+	{
+		if(dim3->value()>=0)
+		{
+			if(dim1->value()>=0)	snprintf(a1,16,"%g",dim1->value());
+			if(dim2->value()>=0)	snprintf(a2,16,"%g",dim2->value());
+			snprintf(a3,16,"%g",dim3->value());
+			snprintf(res,64,"%s(%s,%s,%s)",m.text,a1,a2,a3);
+		}
+		else if(dim2->value()>=0)
+		{
+			if(dim1->value()>=0)	snprintf(a1,16,"%g",dim1->value());
+			snprintf(a2,16,"%g",dim2->value());
+			snprintf(res,64,"%s(%s,%s)",m.text,a1,a2);
+		}
+		else if(dim1->value()>=0)
+		{
+			snprintf(a1,16,"%g",dim1->value());
+			snprintf(res,64,"%s(%s)",m.text,a1);
+		}
+		else	strncpy(res,m.text,64);
+	}
+	return res;
+}
+//-----------------------------------------------------------------------------
+void var_chg_cb(Fl_Widget *, void *)
+{
+	const Fl_Menu_Item m=var_dlg.var->menu()[var_dlg.var->value()];
+	if(m.text[0] && m.user_data())
+	{
+		mglVar *a = (mglVar *)m.user_data();
+		var_dlg.dim1->range(-1,a->nx-1);
+		var_dlg.dim2->range(-1,a->ny-1);
+		var_dlg.dim3->range(-1,a->nz-1);
+	}
+}
+//-----------------------------------------------------------------------------
+void var_in_cb(Fl_Widget *, void *v)
+{
+	Fl_Input *e = (Fl_Input*)v;
+	VarDlg *s = &var_dlg;
+	s->OK = false;
+	s->init();
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	e->value(s->get_result());
+}
+//-----------------------------------------------------------------------------
+void var_dlg_cb(Fl_Widget *, void *v)
+{	var_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void VarDlg::create_dlg()
+{
+	wnd = new Fl_Double_Window(190, 180, gettext("Variable"));
+	var = new Fl_Choice(100, 10, 75, 25, gettext("Variable name"));	// !!!  add variables here !!!
+	var->callback(var_chg_cb);
+	dim1 = new Fl_Spinner(100, 40, 75, 25, gettext("First index"));
+	dim1->range(-1,0);	dim1->value(-1);	dim1->step(1);
+	dim1->tooltip(gettext("Value of first dimensions (-1 for all range)"));
+	dim2 = new Fl_Spinner(100, 70, 75, 25, gettext("Second index"));
+	dim2->range(-1,0);	dim2->value(-1);	dim2->step(1);
+	dim2->tooltip(gettext("Value of second dimensions (-1 for all range)"));
+	dim3 = new Fl_Spinner(100, 100, 75, 25, gettext("Third index"));
+	dim3->range(-1,0);	dim3->value(-1);	dim3->step(1);
+	dim3->tooltip(gettext("Value of third dimensions (-1 for all range)"));
+	Fl_Button *o;
+	o = new Fl_Button(15, 140, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o = new Fl_Return_Button(100, 140, 75, 25, gettext("OK"));	o->callback(var_dlg_cb,wnd);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+const char *cmds[]={
+"plot|area|bars|barh|boxplot|chart|error|mark|region|stem|step|tens|textmark|torus|tube",
+"surf|axial|belt|boxs|cont|contd|contf|dens|fall|grid2|mesh|tile|grad",
+"surf3|cloud|beam|cont3|conta|contf3|contfa|dens3|densa|grid3|grida",
+"map|stfa|surfa|surfc|tile|surf3a|surf3c",
+"flow|pipe|traj|vect|vectc|vectl|dew",
+"contx|conty|contz|contfx|contfy|contfz|densx|densy|densz|triplot|tricont|quadplot|crust|dots",
+"text|title|fgets|legend|addlegend|clearlegend|legendbox",
+"new|var|copy|delete|insert|read|readmat|readall|readhdf|save|savehdf|export|import|info|idset",
+"fill|fillsample|modify|put|crop|extend|rearrange|squeeze|transpose|cumsum|diff|diff2|sinfft|cosfft|hankel|envelop|integrate|mirror|norm|normsl|sew|smooth|swap|roll|addto|subto|divto|multo",
+"combine|evaluate|max|min|hist|jacobian|momentum|resize|sum|trace|transform|transforma|stfad|pde|qo2d|ray",
+"axis|box|colorbar|grid|xlabel|ylabel|zlabel|tlabel",
+"alpha|alphadef|transparent|transptype|ambient|light|fog|arrowsize|barwidth|linewidth|marksize|plotfactor|zoom|cut|axialdir|mesgnum|font|palette|rotatetext",
+"axis|ranges|caxis|crange|xrange|yrange|zrange|origin|ternary|adjust|ctick|xtick|ytick|ztick|ticklen|tickstl",
+"subplot|inplot|rotate|aspect|columnplot|perspective",
+"call|func|chdir|define|if|elseif|else|endif|for|next|once|stop|write|setsize",
+"fit|fits|putsfit",
+"fplot|fsurf|ball|cone|curve|drop|facex|facey|facez|line|rect|sphere"};
+const char *first[]={"plot", "surf", "surf3", "map", "flow", "contx", "text", "new", "fill", "combine", "alpha", "axis", "subplot", "call", "fit", "fplot"};
+const char *cmd_types="1D plots|2D plots|3D plots|Dual plots|Vector plots|Other plots|Text and legend|Create data and I-O|Data handling|Data extraction|Axis and colorbar|General setup|Axis setup|Scale and rotate|Program flow|Nonlinear fitting|Primitives";
+//-----------------------------------------------------------------------------
+void data_file(char *fn)
+{
+	static int num=0;
+	static char name[32], res[256];
+	snprintf(name,32,"mgl_%d",num);	num++;
+	mglData *v = Parse->AddVar(name);
+	v->Read(fn);
+	if(v->nz>1)
+		snprintf(res,256,"#read %s '%s'\nrotate 40 60\ncrange %s\nbox\nsurf3 %s\n", name, fn, name, name);
+	else if(v->ny>1)
+		snprintf(res,256,"#read %s '%s'\nrotate 40 60\ncrange %s\nzrange %s\nbox\nsurf %s\n", name, fn, name, name, name);
+	else
+		snprintf(res,256,"#read %s '%s'\nyrange %s\nbox\nplot %s\n", name, fn, name, name);
+	textbuf->text(res);
+}
+//-----------------------------------------------------------------------------
+//
+//			New Command dialog
+//
+//-----------------------------------------------------------------------------
+struct CmdDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Choice *type, *cmd;
+	Fl_Input *var_x, *var_y, *var_z, *var_u, *var_v, *var_w;
+	Fl_Box *fmt, *dsc;
+	Fl_Input *stl, *zval, *par1, *par2, *opt;
+	Fl_Help_View *help;
+
+	CmdDlg()	{	memset(this,0,sizeof(CmdDlg));	create_dlg();	}
+	~CmdDlg()	{	delete wnd;	}
+	void create_dlg();
+	char *get_result();
+} cmd_dlg;
+//-----------------------------------------------------------------------------
+void cmd_dlg_cb(Fl_Widget *, void *v)	// add variables checking
+{	cmd_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void type_cmd_cb(Fl_Widget *, void *)
+{
+	int val = cmd_dlg.type->value();
+	if(val>=0 && val<16)
+	{
+		cmd_dlg.cmd->clear();	cmd_dlg.cmd->add(cmds[val]);
+		cmd_dlg.dsc->copy_label(Parse->CmdDesc(first[val]));
+		cmd_dlg.fmt->copy_label(Parse->CmdFormat(first[val]));
+
+		static char str[300];	// load help for command
+#ifdef WIN32
+		snprintf(str,300,"%s\\mgl_en.html#%s",docdir,first[val]);
+#else
+		snprintf(str,300,"%s/mgl_en.html#%s",docdir,first[val]);
+#endif
+		cmd_dlg.help->load(str);
+	}
+	cmd_dlg.cmd->value(0);
+}
+//-----------------------------------------------------------------------------
+void desc_cmd_cb(Fl_Widget *, void *)
+{
+	const char *name = cmd_dlg.cmd->mvalue()->text;
+	cmd_dlg.dsc->copy_label(Parse->CmdDesc(name));
+	cmd_dlg.fmt->copy_label(Parse->CmdFormat(name));
+
+	static char str[300];	// load help for command
+#ifdef WIN32
+	snprintf(str,300,"%s\\mgl_en.html#%s",docdir,name);
+#else
+	snprintf(str,300,"%s/mgl_en.html#%s",docdir,name);
+#endif
+	cmd_dlg.help->load(str);
+}
+//-----------------------------------------------------------------------------
+void CmdDlg::create_dlg()
+{
+	Fl_Button *o;
+	wnd = new Fl_Double_Window(500, 450, gettext("Command properties"));
+	type = new Fl_Choice(90, 10, 170, 25, gettext("Type of plot"));
+	type->tooltip(gettext("Select one of general types of plot"));
+	cmd = new Fl_Choice(350, 10, 100, 25, gettext("Command"));
+	cmd->tooltip(gettext("Select kind of plot in this group"));
+
+	fmt = new Fl_Box(0, 40, 500, 25);
+	fmt->box(UDAV_DOWN_BOX);
+	fmt->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+	fmt->tooltip(gettext("Format of command and its arguments"));
+	dsc = new Fl_Box(0, 70, 500, 25);
+	dsc->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+	dsc->tooltip(gettext("Short command description"));
+
+	type->callback(type_cmd_cb,cmd);	cmd->callback(desc_cmd_cb,0);
+	type->add(gettext(cmd_types));		type_cmd_cb(0,0);
+
+	var_x = new Fl_Input(15, 115, 50, 25, "X");	var_x->align(FL_ALIGN_TOP);
+	o = new Fl_Button(65, 115, 25, 25, "..");	o->callback(var_in_cb,var_x);
+	var_y = new Fl_Input(95, 115, 50, 25, "Y");	var_y->align(FL_ALIGN_TOP);
+	o = new Fl_Button(145, 115, 25, 25, "..");	o->callback(var_in_cb,var_y);
+	var_z = new Fl_Input(175, 115, 50, 25, "Z");	var_z->align(FL_ALIGN_TOP);
+	o = new Fl_Button(225, 115, 25, 25, "..");	o->callback(var_in_cb,var_z);
+	var_u = new Fl_Input(255, 115, 50, 25, gettext("Vx or A"));	var_u->align(FL_ALIGN_TOP);
+	o = new Fl_Button(305, 115, 25, 25, "..");	o->callback(var_in_cb,var_u);
+	var_v = new Fl_Input(335, 115, 50, 25, gettext("Vy or C"));	var_v->align(FL_ALIGN_TOP);
+	o = new Fl_Button(385, 115, 25, 25, "..");	o->callback(var_in_cb,var_v);
+	var_w = new Fl_Input(415, 115, 50, 25, "Vz");var_w->align(FL_ALIGN_TOP);
+	o = new Fl_Button(465, 115, 25, 25, "..");	o->callback(var_in_cb,var_w);
+
+	stl = new Fl_Input(15, 165, 50, 25, gettext("Style"));
+	stl->align(FL_ALIGN_TOP);	stl->tooltip(gettext("String argument with command style (or scheme or font)"));
+	o = new Fl_Button(65, 165, 25, 25, "..");	o->callback(style_in_cb, stl);
+
+	zval = new Fl_Input(95, 165, 75, 25, gettext("zVal or sVal"));
+	zval->align(FL_ALIGN_TOP);
+	zval->tooltip(gettext("Z-value or value of slice.\nKeep empty for default value"));
+	par1 = new Fl_Input(175, 165, 75, 25, gettext("Text or dir"));
+	par1->align(FL_ALIGN_TOP);
+	par1->tooltip(gettext("Text (in text command) or direction (in cont3, contf3, dens3)"));
+	par2 = new Fl_Input(255, 165, 75, 25, gettext("Number"));
+	par2->align(FL_ALIGN_TOP);
+	par2->tooltip(gettext("Number of contours in cont* commands"));
+
+	opt = new Fl_Input(15, 215, 290, 25, gettext("Options"));
+	opt->align(FL_ALIGN_TOP_LEFT);
+	opt->tooltip(gettext("Command options"));
+	o = new Fl_Button(305, 215, 25, 25, "..");			o->callback(option_in_cb, opt);
+
+	o = new Fl_Button(405, 180, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb, wnd);
+	o = new Fl_Return_Button(405, 215, 75, 25, gettext("OK"));	o->callback(cmd_dlg_cb, wnd);
+
+	help = new Fl_Help_View(0, 250, 500, 200);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+char *CmdDlg::get_result()
+{
+	static char res[1024],buf[128];
+	res[0]=0;
+	const char *cn = cmd->mvalue()->text;
+
+	bool sl3 = !strcmp(cn,"cont3") || !strcmp(cn,"contf3") || !strcmp(cn,"dens3");
+	strcpy(res,"\n");	strncat(res,cn,1022);
+	snprintf(buf,128,"%s%s%s%s%s%s%s%s%s%s%s%s", var_x->value()[0]?" ":"", var_x->value(),
+		var_y->value()[0]?" ":"", var_y->value(), var_z->value()[0]?" ":"", var_z->value(),
+		var_u->value()[0]?" ":"", var_u->value(), var_v->value()[0]?" ":"", var_v->value(),
+		var_w->value()[0]?" ":"", var_w->value());
+	strcat(res,buf);
+
+	if(!strcmp(cn,"text") && par1->value()[0])
+	{	strcat(res," '");	strcat(res,par1->value());	strcat(res,"'");	}
+	if(sl3 && !par1->value()[0])
+	{
+		strcat(res," 'x'");
+		fl_message(gettext("You should specify direction.\nDirection 'x' is selected by default"));
+		if(zval->value()[0])
+		{	snprintf(buf,128," %d",atoi(zval->value()));	strcat(res,buf);	}
+	}
+	if(sl3 && par1->value()[0])
+	{
+		strcat(res," '");	strcat(res,par1->value());	strcat(res,"'");
+		if(zval->value()[0])
+		{	snprintf(buf,128," %d",atoi(zval->value()));	strcat(res,buf);	}
+	}
+	if(stl->value()[0])
+	{	strcat(res," '");	strcat(res,stl->value());	strcat(res,"'");	}
+	if(!sl3 && zval->value()[0])
+	{	snprintf(buf,128," %d",atoi(zval->value()));	strcat(res,buf);	}
+	if(!sl3 && par2->value()[0])
+	{	snprintf(buf,128," %d",atoi(par2->value()));	strcat(res,buf);	}
+	if(opt->value()[0])	strcat(res,opt->value());
+//	strcat(res,"\n");
+	return res;
+}
+//-----------------------------------------------------------------------------
+void command_cb(Fl_Widget *, void *v)
+{
+	CmdDlg *s = &cmd_dlg;
+	ScriptWindow* e = (ScriptWindow*)v;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// replace current selection
+	{
+		long i=e->editor->insert_position(), j=textbuf->line_end(i);
+		e->editor->insert_position(j);
+		e->editor->insert(s->get_result());
+	}
+}
+//-----------------------------------------------------------------------------
+void plot_dat_cb(Fl_Widget *, void *)
+{
+	CmdDlg *s = &cmd_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// replace current selection
+		textbuf->insert(textbuf->length(), s->get_result());
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/editor.cpp b/mgllab/editor.cpp
new file mode 100644
index 0000000..89122e0
--- /dev/null
+++ b/mgllab/editor.cpp
@@ -0,0 +1,515 @@
+/* editor.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <ctype.h>
+#include <errno.h>
+#ifdef __MWERKS__
+# define FL_DLL
+#endif
+#include "udav.h"
+//-----------------------------------------------------------------------------
+int		changed = 0;
+char	filename[256] = "";
+Fl_Text_Buffer	*textbuf = 0;
+void data_file(char *v);
+//-----------------------------------------------------------------------------
+// Syntax highlighting stuff...
+Fl_Text_Buffer	 *stylebuf = 0;
+Fl_Text_Display::Style_Table_Entry styletable[] = {	// Style table
+		{ FL_BLACK,		FL_COURIER,			14, 0 },	// A - Plain
+		{ FL_DARK_GREEN,FL_COURIER_ITALIC,	14, 0 },	// B - Line comments
+		{ FL_BLUE,		FL_COURIER,			14, 0 },	// C - Number
+		{ FL_RED,		FL_COURIER,			14, 0 },	// D - Strings
+		{ FL_DARK_BLUE,	FL_COURIER_BOLD,	14, 0 },	// E - Usual ommand
+		{ FL_DARK_CYAN,	FL_COURIER_BOLD,	14, 0 },	// F - Flow command
+		{ FL_DARK_MAGENTA,	FL_COURIER_BOLD,14, 0 },	// G - New-data command
+		{ FL_DARK_RED,	FL_COURIER,	14, 0 },	// H - Option
+		{ FL_DARK_GREEN,FL_COURIER_BOLD,	14, 0 }};  // I - Inactive command
+//-----------------------------------------------------------------------------
+bool is_sfx(const char *s)	// suffix
+{
+	register long i,n=strlen(s);
+	for(i=0;i<n && s[i]>='a';i++);
+	if(i==1 && s[0]=='a')	return true;
+	if(i==2 && strchr("axyz",s[1]) && strchr("nmawsk",s[0]))	return true;
+	if(i==3 && (!strncmp("fst",s,3) || !strncmp("lst",s,3) || !strncmp("max",s,3) ||
+				!strncmp("min",s,3) || !strncmp("sum",s,3)))
+		return true;
+	return false;
+//	char *t = new char[i+1];	memcpy(t,s,i*sizeof(char));	t[i]=0;
+}
+//-----------------------------------------------------------------------------
+bool is_opt(const char *s)	// option
+{
+	const char *o[]={"xrange","yrange","zrange","crange","alpha",
+					"cut","value","meshnum","size","legend"};
+	int l[10] = {6, 6, 6, 6, 5, 3, 5, 7, 4, 6};
+	register long i;
+	for(i=0;i<10;i++)	if(!strncmp(o[i],s,l[i]) && s[l[i]]<=' ')	return true;
+	return false;
+}
+//-----------------------------------------------------------------------------
+bool is_num(const char *s)	// number
+{
+	register long i,n=strlen(s);
+	if(s[0]==':' && (s[1]<=' ' || s[1]==';'))	return true;
+	if(n>=2 && !strncmp("pi",s,2) && (s[2]<=' ' || s[2]==';' || s[2]==':'))	return true;
+	if(n>=2 && !strncmp("on",s,2) && (s[2]<=' ' || s[2]==';' || s[2]==':'))	return true;
+	if(n>=3 && !strncmp("off",s,3) && (s[3]<=' ' || s[3]==';' || s[2]==':'))	return true;
+	if(n>=3 && !strncmp("nan",s,3) && (s[3]<=' ' || s[3]==';' || s[2]==':'))	return true;
+	for(i=0;i<n;i++)
+	{
+		if(s[i]<=' ' || s[i]==';')	break;
+		if(!strchr("+-.eE0123456789",s[i]))	return false;
+	}
+	return true;
+//	char *t = new char[i+1];	memcpy(t,s,i*sizeof(char));	t[i]=0;
+}
+//-----------------------------------------------------------------------------
+char is_cmd(const char *s)	// command
+{
+	register long i,n=strlen(s)+1;
+	char res=0, *w=new char[n];	strcpy(w,s);
+	for(i=0;i<n;i++)	if(!isalnum(s[i]))	w[i]=0;
+	int rts = Parse->CmdType(w);
+	if(rts==5)		res = 'G';
+	else if(rts==7)	res = 'F';
+	else if(rts)	res = 'E';
+	delete []w;		return res;
+}
+//-----------------------------------------------------------------------------
+// Parse text and produce style data.
+void style_parse(const char *text, char *style, int /*length*/)
+{
+	register long i;
+	long n=strlen(text);
+	bool nl=true, arg=true;
+	// Style letters:
+	// A - Plain
+	// B - Line comments
+	// C - Number
+	// D - Strings
+	// E - Usual command
+	// F - Flow command
+	// G - New data command
+	// H - Option
+
+	for(i=0;i<n;i++)
+	{
+		style[i] = 'A';
+		if(text[i]=='#')	// comment
+			for(;i<n && text[i]!='\n';i++)	style[i]='B';
+		if(text[i]=='\'')	// string
+		{
+			arg = false;	style[i]='D';	i++;
+			for(;i<n && text[i]!='\n' && text[i]!='\'';i++)	style[i]='D';
+			style[i]='D';
+		}
+		if(text[i]=='\n' || text[i]==':')	{	nl=true;	style[i]='A';	continue;	}
+		char r = is_cmd(text+i);
+		if(nl && r)	// command name
+		{	for(;i<n && isalnum(text[i]);i++)	style[i]=r;		i--;	}
+		if(text[i]<=' ' || text[i]==';')	{	arg = true;	continue;	}
+		if(arg && is_opt(text+i))	// option
+		{	for(;i<n && isalpha(text[i]);i++)	style[i]='H';	i--;	}
+		if(arg && is_num(text+i))	// number
+		{
+			if(text[i]==':' && (isspace(text[i+1]) || text[i+1]==':'))
+				style[i]='C';
+			else for(;i<n && strchr("+-.eE0123456789pionaf",text[i]) ;i++)
+				style[i]='C';
+			i--;
+		}
+		if(text[i]=='.' && is_sfx(text+i+1))	// option (suffix)
+		{
+			style[i]='H';	i++;
+			for(;i<n && isalpha(text[i]);i++)	style[i]='H';
+		}
+		nl = arg = false;
+	}
+}
+//-----------------------------------------------------------------------------
+// Initialize the style buffer...
+void style_init(void)
+{
+	char *style = new char[textbuf->length() + 1];
+	char *text = textbuf->text();
+	memset(style, 'A', textbuf->length());
+	style[textbuf->length()] = '\0';
+	if (!stylebuf) stylebuf = new Fl_Text_Buffer(textbuf->length());
+	style_parse(text, style, textbuf->length());
+	stylebuf->text(style);
+	delete[] style;
+	free(text);
+}
+//-----------------------------------------------------------------------------
+// Update unfinished styles.
+void style_unfinished_cb(int, void*) {}
+//-----------------------------------------------------------------------------
+// Update the style buffer...
+void style_update(int pos,		// Position of update
+				int nInserted,	// Number of inserted chars
+				int nDeleted,	// Number of deleted chars
+				int	/*nRestyled*/,			// Number of restyled chars
+				const char */*deletedText*/,// Text that was deleted
+				void *cbArg)	// Callback data
+{
+	long	start, end;	// Start and end of text
+	char last,		// Last style on line
+		*style,		// Style data
+		*text;		// Text data
+
+	// If this is just a selection change, just unselect the style buffer...
+	if (nInserted == 0 && nDeleted == 0) {	stylebuf->unselect();	return;  }
+	// Track changes in the text buffer...
+	if (nInserted > 0)
+	{
+		// Insert characters into the style buffer...
+		style = new char[nInserted + 1];
+		memset(style, 'A', nInserted);
+		style[nInserted] = '\0';
+
+		stylebuf->replace(pos, pos + nDeleted, style);
+		delete[] style;
+	}
+	else	// Just delete characters in the style buffer...
+		stylebuf->remove(pos, pos + nDeleted);
+
+	// Select the area that was just updated to avoid unnecessary callbacks...
+	stylebuf->select(pos, pos + nInserted - nDeleted);
+
+	// Re-parse the changed region; we do this by parsing from the
+	// beginning of the previous line of the changed region to the end of
+	// the line of the changed region...  Then we check the last
+	// style character and keep updating if we have a multi-line
+	// comment character...
+	start = textbuf->line_start(pos);
+	end   = textbuf->line_end(pos + nInserted);
+	text  = textbuf->text_range(start, end);
+	style = stylebuf->text_range(start, end);
+	if (start==end)	last = 0;
+	else	last = style[end-start-1];
+	style_parse(text, style, end - start);
+	stylebuf->replace(start, end, style);
+	((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
+
+	if (start==end || last != style[end-start-1])
+	{
+		// Either the user deleted some text, or the last character on
+		// the line changed styles, so reparse the remainder of the buffer...
+		free(text);	free(style);
+
+		end   = textbuf->length();
+		text  = textbuf->text_range(start, end);
+		style = stylebuf->text_range(start, end);
+		style_parse(text, style, end - start);
+		stylebuf->replace(start, end, style);
+		((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
+	}
+	free(text);	free(style);
+}
+//-----------------------------------------------------------------------------
+ScriptWindow::ScriptWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t)
+{
+	replace_dlg = new Fl_Window(300, 105, gettext("Replace"));
+	replace_find = new Fl_Input(80, 10, 210, 25, gettext("Find:"));
+	replace_find->align(FL_ALIGN_LEFT);
+
+	replace_with = new Fl_Input(80, 40, 210, 25, gettext("Replace:"));
+	replace_with->align(FL_ALIGN_LEFT);
+
+	replace_all = new Fl_Button(10, 70, 90, 25, gettext("Replace All"));
+	replace_all->callback((Fl_Callback *)replall_cb, this);
+	replace_all->box(UDAV_UP_BOX);	replace_all->down_box(UDAV_DOWN_BOX);
+
+	replace_next = new Fl_Return_Button(105, 70, 120, 25, "Replace Next");
+	replace_next->callback((Fl_Callback *)replace2_cb, this);
+	replace_next->box(UDAV_UP_BOX);	replace_next->down_box(UDAV_DOWN_BOX);
+
+	replace_cancel = new Fl_Button(230, 70, 60, 25, gettext("Cancel"));
+	replace_cancel->callback((Fl_Callback *)replcan_cb, this);
+	replace_cancel->box(UDAV_UP_BOX);	replace_cancel->down_box(UDAV_DOWN_BOX);
+
+	replace_dlg->end();
+	replace_dlg->set_non_modal();
+	editor = 0;		*search = 0;
+
+	setup_dlg = new SetupDlg;
+	setup_dlg->CreateDlg();
+}
+//-----------------------------------------------------------------------------
+ScriptWindow::~ScriptWindow()
+{
+	delete replace_dlg;
+	delete setup_dlg->wnd;
+}
+//-----------------------------------------------------------------------------
+int check_save(void)
+{
+  if (!changed) return 1;
+  int r = fl_choice(gettext("The current file has not been saved.\n"
+					"Would you like to save it now?"),
+					gettext("Cancel"), gettext("Save"), gettext("Don't Save"));
+  if(r==1)	{	save_cb(0,0);	return !changed;	} // Save the file...
+  return (r==2) ? 1 : 0;
+}
+//-----------------------------------------------------------------------------
+int loading = 0;
+void load_file(char *newfile, int ipos)
+{
+	long len = strlen(newfile);
+	pref.set("last_file",newfile);
+	if(ipos==-1 && (!strcmp(newfile+len-4,".dat") || !strcmp(newfile+len-4,".csv")))
+	{
+		data_file(newfile);
+		strncpy(newfile+len-4,".mgl",4);
+		strncpy(filename, newfile,256);
+	}
+	else
+	{
+		loading = 1;
+		int insert = (ipos != -1);
+		changed = insert;
+		if(!insert) *filename=0;
+		long r;
+		if(!insert)	r = textbuf->loadfile(newfile);
+		else r = textbuf->insertfile(newfile, ipos);
+
+		char *t = textbuf->text();
+#ifndef WIN32
+		for(size_t i=0;i<strlen(t);i++)	if(t[i]=='\r')	t[i]=' ';
+		textbuf->text(t);
+#endif
+		fill_animate(t);	free(t);
+
+		if (r)
+			fl_alert(gettext("Error reading from file \'%s\':\n%s."), newfile, strerror(errno));
+		else	if(!insert)	strncpy(filename, newfile,256);
+		loading = 0;
+		textbuf->call_modify_callbacks();
+	}
+}
+//-----------------------------------------------------------------------------
+void save_file(char *newfile)
+{
+	pref.set("last_file",newfile);
+	if (textbuf->savefile(newfile))
+		fl_alert(gettext("Error writing to file \'%s\':\n%s."), newfile, strerror(errno));
+	else
+		strncpy(filename, newfile,256);
+	changed = 0;
+	textbuf->call_modify_callbacks();
+}
+//-----------------------------------------------------------------------------
+void copy_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	Fl_Text_Editor::kf_copy(0, e->editor);
+}
+//-----------------------------------------------------------------------------
+void cut_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	Fl_Text_Editor::kf_cut(0, e->editor);
+}
+//-----------------------------------------------------------------------------
+void delete_cb(Fl_Widget*, void*) {	textbuf->remove_selection();	}
+//-----------------------------------------------------------------------------
+void find_cb(Fl_Widget* w, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	const char *val;
+	val = fl_input(gettext("Search String:"), e->search);
+	if (val != NULL) {	strncpy(e->search, val,256);	find2_cb(w, v);	}
+}
+//-----------------------------------------------------------------------------
+void find2_cb(Fl_Widget* w, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	if (e->search[0] == '\0')	{	find_cb(w, v);	return;	}
+
+	int pos = e->editor->insert_position();
+	long found = textbuf->search_forward(pos, e->search, &pos);
+	if (found) {
+		// Found a match; select and update the position...
+		textbuf->select(pos, pos+strlen(e->search));
+		e->editor->insert_position(pos+strlen(e->search));
+		e->editor->show_insert_position();
+	}
+	else fl_alert(gettext("No occurrences of \'%s\' found!"), e->search);
+}
+//-----------------------------------------------------------------------------
+void changed_cb(int, int nInserted, int nDeleted,int, const char*, void* v)
+{
+	if ((nInserted || nDeleted) && !loading) changed = 1;
+	ScriptWindow *w = (ScriptWindow *)v;
+	set_title(w);
+	if (loading) w->editor->show_insert_position();
+}
+//-----------------------------------------------------------------------------
+void insert_cb(Fl_Widget*, void *v)
+{
+	char *newfile = fl_file_chooser(gettext("Insert File?"), "*", filename);
+	ScriptWindow *w = (ScriptWindow *)v;
+	if (newfile != NULL) load_file(newfile, w->editor->insert_position());
+}
+//-----------------------------------------------------------------------------
+void paste_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	Fl_Text_Editor::kf_paste(0, e->editor);
+}
+//-----------------------------------------------------------------------------
+void replace_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	e->replace_dlg->show();
+}
+//-----------------------------------------------------------------------------
+void replace2_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	const char *find = e->replace_find->value();
+	const char *replace = e->replace_with->value();
+	if (find[0] == '\0')	{	e->replace_dlg->show();	return;	}
+	e->replace_dlg->hide();
+
+	int pos = e->editor->insert_position();
+	long found = textbuf->search_forward(pos, find, &pos);
+	if (found)
+	{
+		// Found a match; update the position and replace text...
+		textbuf->select(pos, pos+strlen(find));
+		textbuf->remove_selection();
+		textbuf->insert(pos, replace);
+		textbuf->select(pos, pos+strlen(replace));
+		e->editor->insert_position(pos+strlen(replace));
+		e->editor->show_insert_position();
+	}
+	else fl_alert(gettext("No occurrences of \'%s\' found!"), find);
+}
+//-----------------------------------------------------------------------------
+void replall_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	const char *find = e->replace_find->value();
+	const char *replace = e->replace_with->value();
+
+	find = e->replace_find->value();
+	if (find[0] == '\0')	{	e->replace_dlg->show();	return;	}
+	e->replace_dlg->hide();
+	e->editor->insert_position(0);
+	long times = 0;
+
+	// Loop through the whole string
+	for (long found = 1; found;)
+	{
+		int pos = e->editor->insert_position();
+		found = textbuf->search_forward(pos, find, &pos);
+		if (found)
+		{
+			// Found a match; update the position and replace text...
+			textbuf->select(pos, pos+strlen(find));
+			textbuf->remove_selection();
+			textbuf->insert(pos, replace);
+			e->editor->insert_position(pos+strlen(replace));
+			e->editor->show_insert_position();
+			times++;
+		}
+	}
+	if (times) fl_message(gettext("Replaced %ld occurrences."), times);
+	else fl_alert(gettext("No occurrences of \'%s\' found!"), find);
+}
+//-----------------------------------------------------------------------------
+void replcan_cb(Fl_Widget*, void* v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	e->replace_dlg->hide();
+}
+//-----------------------------------------------------------------------------
+void view_cb(Fl_Widget*, void*);
+//#include "xpm/window.xpm"
+//#include "xpm/option.xpm"
+//#include "xpm/table.xpm"
+#include "xpm/plot.xpm"
+#include "xpm/help-contents.xpm"
+#include "xpm/edit-cut.xpm"
+#include "xpm/edit-copy.xpm"
+#include "xpm/edit-paste.xpm"
+#include "xpm/edit-find.xpm"
+#include "xpm/document-open.xpm"
+#include "xpm/document-new.xpm"
+#include "xpm/document-save.xpm"
+Fl_Widget *add_editor(ScriptWindow *w)
+{
+	Fl_Window *w1=new Fl_Window(0,30,300,430,0);
+	Fl_Group *g = new Fl_Group(0,0,290,30);
+	Fl_Button *o;
+
+	o = new Fl_Button(0, 1, 25, 25);	o->image(new Fl_Pixmap(document_new_xpm));
+	o->tooltip(gettext("New script"));	o->callback(new_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(25, 1, 25, 25);	o->tooltip(gettext("Open script or data file"));
+	o->image(new Fl_Pixmap(document_open_xpm));	o->callback(open_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(50, 1, 25, 25);	o->tooltip(gettext("Save script to file"));
+	o->image(new Fl_Pixmap(document_save_xpm));	o->callback(save_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+
+	o = new Fl_Button(80, 1, 25, 25);	o->tooltip(gettext("Cut selection to clipboard"));
+	o->image(new Fl_Pixmap(edit_cut_xpm));	o->callback(cut_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(105, 1, 25, 25);	o->tooltip(gettext("Copy selection to clipboard"));
+	o->image(new Fl_Pixmap(edit_copy_xpm));	o->callback(copy_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(130, 1, 25, 25);	o->tooltip(gettext("Paste text from clipboard"));
+	o->image(new Fl_Pixmap(edit_paste_xpm));	o->callback(paste_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(155, 1, 25, 25);	o->tooltip(gettext("Find text"));
+	o->image(new Fl_Pixmap(edit_find_xpm));	o->callback(find_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+
+	o = new Fl_Button(185, 1, 25, 25);	o->tooltip(gettext("Insert MGL command"));
+	o->image(new Fl_Pixmap(plot_xpm));	o->callback(command_cb,w);
+	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+//	o = new Fl_Button(185, 1, 25, 25);	o->tooltip(gettext("Insert command options"));
+//	o->image(new Fl_Pixmap(option_xpm));	o->callback(option_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+//	o = new Fl_Button(210, 1, 25, 25);	o->tooltip(gettext("Edit data array"));
+//	o->image(new Fl_Pixmap(table_xpm));	o->callback(table_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+//	o = new Fl_Button(235, 1, 25, 25);	o->tooltip(gettext("New view window"));
+//	o->image(new Fl_Pixmap(window_xpm));o->callback(view_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(210, 1, 25, 25);	o->tooltip(gettext("Show help window"));
+	o->image(new Fl_Pixmap(help_contents_xpm));	o->callback(help_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	g->end();	g->resizable(0);
+
+	w->editor = new Fl_Text_Editor(0, 28, 300, 400);
+	w->editor->buffer(textbuf);
+	w->editor->highlight_data(stylebuf, styletable, sizeof(styletable) / sizeof(styletable[0]), 'A', style_unfinished_cb, 0);
+	w->editor->textfont(FL_COURIER);
+
+	textbuf->add_modify_callback(style_update, w->editor);
+	textbuf->add_modify_callback(changed_cb, w);
+	textbuf->call_modify_callbacks();
+
+	w1->end();
+	w1->resizable(w->editor);	//w->graph);
+	return w1;
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/grid.cpp b/mgllab/grid.cpp
new file mode 100644
index 0000000..b19caa9
--- /dev/null
+++ b/mgllab/grid.cpp
@@ -0,0 +1,110 @@
+/* grid.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Int_Input.H>
+#include <FL/Fl_Value_Slider.H>
+#include <FL/fl_draw.H>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+Fl_Callback input_cb;
+//-----------------------------------------------------------------------------
+void input_cb(Fl_Widget*, void* v)	{ ((Fl_Data_Table*)v)->set_value(); }
+//-----------------------------------------------------------------------------
+Fl_Data_Table::Fl_Data_Table(int x, int y, int w, int h, const char *l) : Fl_Table(x,y,w,h,l)
+{
+	callback(&event_callback, (void*)this);
+	input = new Fl_Input(w/2,h/2,0,0);
+	input->hide();
+	input->callback(input_cb, (void*)this);
+	input->when(FL_WHEN_ENTER_KEY_ALWAYS);
+	input->maximum_size(16);
+//	(new Fl_Box(9999,9999,0,0))->hide();  // HACK: prevent flickering in Fl_Scroll
+	end();
+}
+//-----------------------------------------------------------------------------
+// Handle drawing all cells in table
+void Fl_Data_Table::draw_cell(TableContext context, int R, int C, int X, int Y, int W, int H)
+{
+	static char s[32];
+	fl_push_clip(X, Y, W, H);
+	switch ( context )
+	{
+	case CONTEXT_COL_HEADER:
+		fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, col_header_color());
+		fl_font(FL_HELVETICA | FL_BOLD, 14);
+		fl_color(FL_BLACK);		snprintf(s,32,"%d",C);
+		fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER);
+		break;
+	case CONTEXT_ROW_HEADER:
+		fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, col_header_color());
+		fl_font(FL_HELVETICA | FL_BOLD, 14);
+		fl_color(FL_BLACK);		snprintf(s,32,"%d",R);
+		fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER);
+		break;
+	case CONTEXT_CELL:
+	    if (R == row && C == col && input->visible())	break;
+		fl_draw_box(FL_THIN_DOWN_BOX, X, Y, W, H, FL_WHITE);
+		fl_pop_clip();
+		fl_push_clip(X+3, Y+3, W-6, H-6);
+		fl_font(FL_HELVETICA, 14);
+		fl_color(FL_BLACK);
+		if(mgl_isnan(data[C+nx*R]))	strcpy(s,"nan");
+		else	snprintf(s,32,"%g",data[C+nx*R]);
+		fl_draw(s, X+3, Y+3, W-6, H-6, FL_ALIGN_RIGHT);
+		break;
+	case CONTEXT_RC_RESIZE:
+		if (!input->visible()) break;
+		find_cell(CONTEXT_TABLE, row, col, X, Y, W, H);
+		if (X!=input->x() || Y!=input->y() || W!=input->w() || H!=input->h())
+			input->resize(X,Y,W,H);
+		break;
+	default:	break;
+	}
+	fl_pop_clip();
+}
+//-----------------------------------------------------------------------------
+void Fl_Data_Table::cell_click()
+{
+    int R = callback_row(), C = callback_col();
+    TableContext context = callback_context();
+
+    if(context==CONTEXT_CELL)
+	{
+		if (input->visible())	//input->do_callback();
+		{
+			const char *s = input->value();
+			data[col + nx*row] = (s[0]==0 || !strcmp(s,"nan")) ? NAN : atof(s);
+		}
+		row = R;		col = C;
+		int XX,YY,WW,HH;
+		find_cell(CONTEXT_CELL, R, C, XX, YY, WW, HH);
+		input->resize(XX,YY,WW,HH);
+		char s[32];
+		if(mgl_isnan(data[C+nx*R]))	strcpy(s,"nan");
+		else	snprintf(s,32,"%g",data[C+nx*R]);
+		input->value(s);	input->show();
+		input->take_focus();
+	}
+}
+//-----------------------------------------------------------------------------
+void Fl_Data_Table::set_value()
+{
+	const char *s = input->value();
+	data[col + nx*row] = (s[0]==0 || !strcmp(s,"nan")) ? NAN : atof(s);
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/help.cpp b/mgllab/help.cpp
new file mode 100644
index 0000000..5f80d31
--- /dev/null
+++ b/mgllab/help.cpp
@@ -0,0 +1,244 @@
+/* help.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "udav.h"
+#include <ctype.h>
+#include <FL/Fl_Select_Browser.H>
+//-----------------------------------------------------------------------------
+void help_cb(Fl_Widget*, void*v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	long i=e->editor->insert_position(), j0=textbuf->line_start(i),j;
+
+	static char str[300];
+	char s[32]="", *buf = textbuf->text();
+	memset(s,0,32*sizeof(char));
+	for(j=j0;!isspace(buf[j]) && buf[j]!='#' && buf[j]!=';' && j<31+j0;j++)
+		s[j-j0] = buf[j];
+	free(buf);
+#ifdef WIN32
+	snprintf(str,300,"%s\\mgl_en.html#%s",docdir,s);
+#else
+	snprintf(str,300,"%s/mgl_en.html#%s",docdir,s);
+#endif
+	e->hd->load(str);
+	if(e->rtab)	e->rtab->value(e->ghelp);
+}
+//-----------------------------------------------------------------------------
+void link_cb(Fl_Widget*, void*v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	static char str[300];
+#ifdef WIN32
+	snprintf(str,300,"%s\\mgl_en.html#%s",docdir,e->link_cmd->value());
+#else
+	snprintf(str,300,"%s/mgl_en.html#%s",docdir,e->link_cmd->value());
+#endif
+	e->hd->load(str);
+	if(e->rtab)	e->rtab->value(e->ghelp);
+}
+//-----------------------------------------------------------------------------
+void example_cb(Fl_Widget*, void*v)
+{
+	static char str[300];
+	ScriptWindow* e = (ScriptWindow*)v;
+#ifdef WIN32
+	snprintf(str,300,"%s\\mgl_en.html\\mgl_en_2.html",docdir);
+#else
+	snprintf(str,300,"%s/mgl_en.html/mgl_en_2.html",docdir);
+#endif
+	e->hd->load(str);	e->rtab->value(e->ghelp);
+	if(e->rtab)	e->rtab->value(e->ghelp);
+}
+//-----------------------------------------------------------------------------
+void help_in_cb(Fl_Widget*, void*v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	e->hd->textsize(e->hd->textsize()+1);
+}
+//-----------------------------------------------------------------------------
+void help_out_cb(Fl_Widget*, void*v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	e->hd->textsize(e->hd->textsize()-1);
+}
+//-----------------------------------------------------------------------------
+#include "xpm/udav.xpm"
+void about_cb(Fl_Widget*, void*)
+{
+	static char s[128];
+	snprintf(s,128,gettext("UDAV v. 2.%g\n(c) Alexey Balakin, 2007\nhttp://udav.sf.net/"), MGL_VER2);
+	Fl_Double_Window* w = new Fl_Double_Window(355, 130, "About UDAV");
+	Fl_Box* o = new Fl_Box(10, 15, 65, 65);
+	o->box(FL_UP_BOX);	o->color(55);	o->image(new Fl_Pixmap(udav_xpm));
+	o = new Fl_Box(85, 15, 260, 65);	o->box(UDAV_DOWN_BOX);
+	o->label(s);
+	Fl_Button *b = new Fl_Return_Button(255, 90, 90, 30, "Close");
+	b->callback(close_dlg_cb,w);
+	b->box(UDAV_UP_BOX);	b->down_box(UDAV_DOWN_BOX);
+	w->end();	w->set_modal();	w->show();
+}
+//-----------------------------------------------------------------------------
+#include "xpm/zoom-out.xpm"
+#include "xpm/zoom-in.xpm"
+#include "xpm/help-faq.xpm"
+Fl_Widget *add_help(ScriptWindow *w)
+{
+	Fl_Window *w1=new Fl_Window(300,30,630,430,0);
+	Fl_Group *g = new Fl_Group(0,0,290,30);
+	Fl_Button *o;
+
+	w->link_cmd = new Fl_Input(0,1,150,25);
+	w->link_cmd->when(FL_WHEN_CHANGED);
+	w->link_cmd->callback(link_cb,w);
+
+	o = new Fl_Button(155, 1, 25, 25);	o->tooltip(gettext("MGL samples and hints"));
+	o->image(new Fl_Pixmap(help_faq_xpm));	o->callback(example_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(180, 1, 25, 25);	o->tooltip(gettext("Increase font size"));
+	o->image(new Fl_Pixmap(zoom_in_xpm));	o->callback(help_in_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(205, 1, 25, 25);	o->tooltip(gettext("Decrease font size"));
+	o->image(new Fl_Pixmap(zoom_out_xpm));	o->callback(help_out_cb,w);
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+
+	g->end();	g->resizable(0);
+
+	w->hd = new Fl_Help_View(0,28,630,400);
+	w1->end();	link_cb(w,w);
+	w1->resizable(w->hd);	return w1;
+}
+//-----------------------------------------------------------------------------
+void mem_dlg_cb0(Fl_Widget *, void *v)
+{	((ScriptWindow*)v)->mem_pressed(0);	}
+//-----------------------------------------------------------------------------
+void mem_dlg_cb1(Fl_Widget *, void *v)
+{	((ScriptWindow*)v)->mem_pressed(1);	}
+//-----------------------------------------------------------------------------
+void mem_dlg_cb2(Fl_Widget *, void *v)
+{	((ScriptWindow*)v)->mem_pressed(2);	}
+//-----------------------------------------------------------------------------
+void mem_dlg_cb3(Fl_Widget *, void *v)
+{	((ScriptWindow*)v)->mem_pressed(3);	}
+//-----------------------------------------------------------------------------
+void mem_update_cb(Fl_Widget *, void *v)
+{	((ScriptWindow*)v)->mem_init();	}
+//-----------------------------------------------------------------------------
+Fl_Widget *add_mem(ScriptWindow *w)
+{
+	static int widths[] = {220,205,0};
+	Fl_Button *o;
+	Fl_Box *b;
+//	wnd = new Fl_Double_Window(335, 405, gettext("Data browser"));
+	Fl_Window *wnd = new Fl_Window(300,30,630,430,0);
+
+//	Fl_Group *g = new Fl_Group(10,10,610,395);
+	b = new Fl_Box(0, 10, 630, 25, gettext("Existed data arrays"));	b->labeltype(FL_ENGRAVED_LABEL);
+	b = new Fl_Box(0, 35, 220, 25, gettext("name"));
+	b->box(FL_THIN_UP_BOX);	b->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+	b = new Fl_Box(220, 35, 205, 25, gettext("dimensions"));
+	b->box(FL_THIN_UP_BOX);	b->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+	b = new Fl_Box(425, 35, 205, 25, gettext("mem. usage"));
+	b->box(FL_THIN_UP_BOX);	b->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+
+	w->var = new Fl_Select_Browser(0, 60, 630, 335);	w->var->column_char('\t');
+	w->var->align(FL_ALIGN_TOP);	w->var->column_widths(widths);
+	w->var->tooltip(gettext("List of available data."));
+//	g->end();
+
+	o = new Fl_Button(10, 400, 95, 25, gettext("Edit"));	o->callback(mem_dlg_cb0,w);
+	o->tooltip(gettext("Open table with selected data for editing."));
+	o = new Fl_Button(120, 400, 95, 25, gettext("Plot"));	o->callback(mem_dlg_cb1,w);
+	o->tooltip(gettext("Plot selected data."));
+	o = new Fl_Button(230, 400, 95, 25, gettext("Delete"));	o->callback(mem_dlg_cb2,w);
+	o->tooltip(gettext("Delete selected data."));
+	o = new Fl_Button(340, 400, 95, 25, gettext("New"));	o->callback(mem_dlg_cb3,w);
+	o->tooltip(gettext("Open dialog for new data creation."));
+	o = new Fl_Button(450, 400, 95, 25, gettext("Refresh"));	o->callback(mem_update_cb,w);
+	o->tooltip(gettext("Refresh list of variables."));
+//	o = new Fl_Button(120, 335, 95, 25, gettext("Load"));	o->callback(mem_dlg_cb,(void *)4);
+//	o = new Fl_Button(230, 335, 95, 25, gettext("Save"));	o->callback(mem_dlg_cb,(void *)5);
+//	o = new Fl_Button(10, 370, 95, 25, gettext("Update"));	o->callback(mem_upd_cb,0);
+	wnd->end();	wnd->resizable(w->var);	return wnd;
+}
+//-----------------------------------------------------------------------------
+void ScriptWindow::mem_init()
+{
+	char str[128];
+	var->clear();
+	mglVar *v=Parse->FindVar("");
+	while(v)
+	{
+		snprintf(str,128,"%ls\t%ld*%ld*%ld\t%ld\t", v->s.c_str(), v->nx, v->ny, v->nz, sizeof(mreal)*v->nx*v->ny*v->nz);
+		var->add(str,v);
+		v = v->next;
+	}
+}
+//-----------------------------------------------------------------------------
+void ScriptWindow::mem_pressed(int kind)
+{
+	TableWindow *w;
+	int ind = var->value();
+	mglVar *v = (mglVar *)var->data(ind);
+	static char res[128];
+	if(!v && kind!=3)	return;
+	if(kind==0)
+	{
+		w = (TableWindow *)v->o;
+		if(!w)
+		{
+			char ss[1024];
+			wcstombs(ss,v->s.c_str(),1024);	ss[v->s.length()]=0;
+			ltab->begin();
+			Fl_Group *gg = new Fl_Group(0,30,300,430);
+			w = new TableWindow(0,30,300,430);
+			gg->label(ss);	gg->end();	ltab->end();
+		}
+		w->update(v);	ltab->value(w->parent());	w->show();
+	}
+	else if(kind==1)
+	{
+		if(v->nz>1)		snprintf(res,128,"box\nsurf3 %ls\n",v->s.c_str());
+		else if(v->ny>1)	snprintf(res,128,"box\nsurf %ls\n",v->s.c_str());
+		else				snprintf(res,128,"box\nplot %ls\n",v->s.c_str());
+		textbuf->text(res);
+	}
+	else if(kind==2)
+		Parse->DeleteVar(v->s.c_str());
+	else if(kind==3)
+	{
+		const char *name = fl_input(gettext("Enter name for new variable"),"dat");
+		if(!name)	return;
+		v = Parse->AddVar(name);
+
+		ltab->begin();
+		Fl_Group *gg = new Fl_Group(0,30,300,430);
+		w = new TableWindow(0,30,300,430);
+		gg->label(name);	gg->end();	ltab->end();
+		w->update(v);	ltab->value(w->parent());	w->show();
+	}
+	mem_init();
+}
+//-----------------------------------------------------------------------------
+void variables_cb(Fl_Widget *, void *v)
+{
+/*	MemDlg *s = &mem_dlg;
+	s->wnd->set_modal();
+	s->init();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();*/
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/main.cpp b/mgllab/main.cpp
new file mode 100644
index 0000000..cc67645
--- /dev/null
+++ b/mgllab/main.cpp
@@ -0,0 +1,304 @@
+/* main.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+#ifndef MGL_DOC_DIR
+#ifdef WIN32
+#define MGL_DOC_DIR ""
+#else
+#define MGL_DOC_DIR "/usr/local/share/doc/mathgl/"
+#endif
+#endif
+//-----------------------------------------------------------------------------
+char	title[256];
+int num_windows = 0, auto_exec=1, plastic_scheme=1, internal_font=0;
+Fl_Preferences pref(Fl_Preferences::USER,"abalakin","mgllab");
+char *docdir=0;
+//-----------------------------------------------------------------------------
+void set_title(Fl_Window* w)
+{
+	if (filename[0] == '\0') strcpy(title, "Untitled");
+	else
+	{
+		char *slash;
+		slash = strrchr(filename, '/');
+#ifdef WIN32
+		if (slash == NULL) slash = strrchr(filename, '\\');
+#endif
+		if (slash != NULL) strncpy(title, slash + 1,256);
+		else strncpy(title, filename,256);
+	}
+	if (changed) strcat(title, gettext(" (modified)"));
+	w->label(title);
+}
+//-----------------------------------------------------------------------------
+void fname_cb(Fl_Widget*, void *v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	char *file = fl_file_chooser(gettext("Insert File Name?"), gettext("All Files (*)"), 0);
+	if(file)
+	{
+		char *str = new char[strlen(file)+4];
+		snprintf(str,strlen(file)+4," '%s'",file);
+		e->editor->insert(str);
+		delete []str;
+	}
+}
+//-----------------------------------------------------------------------------
+void new_cb(Fl_Widget*, void*)
+{
+	if (!check_save()) return;
+	filename[0] = '\0';
+	textbuf->select(0, textbuf->length());
+	textbuf->remove_selection();
+	changed = 0;
+	textbuf->call_modify_callbacks();
+}
+//-----------------------------------------------------------------------------
+void open_cb(Fl_Widget*, void *v)
+{
+	if (!check_save()) return;
+	char *lastname=0;
+	if(*filename==0)	{	pref.get("last_file",lastname,"");	strncpy(filename, lastname,256);	}
+	char *newfile = fl_file_chooser(gettext("Open File?"),
+		gettext("MGL Files (*.mgl)\tDAT Files (*.{dat,csv})\tAll Files (*)"), filename);
+	if(lastname)	free(lastname);
+	if(newfile != NULL)
+	{
+		load_file(newfile, -1);
+		if(auto_exec)	((ScriptWindow*)v)->graph->update();
+	}
+}
+//-----------------------------------------------------------------------------
+void close_cb(Fl_Widget*, void* v)
+{
+	Fl_Window* w = (Fl_Window*)v;
+	if (num_windows == 1 && !check_save())	return;
+
+	w->hide();
+	textbuf->remove_modify_callback(changed_cb, w);
+	delete w;
+	num_windows--;
+	if (!num_windows) exit(0);
+}
+//-----------------------------------------------------------------------------
+void quit_cb(Fl_Widget*, void*)
+{
+	if (changed && !check_save())	return;
+	exit(0);
+}
+//-----------------------------------------------------------------------------
+void save_cb(Fl_Widget*w, void*v)
+{
+	if (filename[0] == '\0')	{	saveas_cb(w,v);	return;	}	// No filename - get one!
+	else save_file(filename);
+}
+//-----------------------------------------------------------------------------
+void saveas_cb(Fl_Widget*, void*)
+{
+	char *newfile, *fname=0;
+	FILE *fp=0;
+	while(1)
+	{
+		newfile = fl_file_chooser(gettext("Save File As?"), "*.mgl", filename);
+		if(!newfile || !newfile[0])	break;
+		if(!strchr(newfile,'.'))
+		{
+			if(fname)	delete []fname;
+			fname = new char[strlen(newfile)+5];
+			strcpy(fname,newfile);	strcat(fname,".mgl");
+			newfile = fname;
+		}
+		fp = fopen(newfile,"r");
+		if(fp)
+		{
+			fclose(fp);
+			if(fl_choice(gettext("File is exesist. Overwrite it?"),0,gettext("No"),gettext(" Yes "))==2)
+				break;
+		}
+		else	break;
+	}
+	if (newfile != NULL)	save_file(newfile);
+	if(fname)	delete []fname;
+}
+//-----------------------------------------------------------------------------
+ScriptWindow *new_view();
+void view_cb(Fl_Widget*, void*)
+{	Fl_Window* w = new_view();	w->show();	}
+//-----------------------------------------------------------------------------
+void hint_cb(Fl_Widget*, void*)	{}
+//-----------------------------------------------------------------------------
+Fl_Menu_Item menuitems[] = {
+//	{ gettext("File"), 0, 0, 0, FL_SUBMENU },
+	{ gettext("File/New File"),			0, new_cb },
+	{ gettext("File/Open File..."),		FL_CTRL + 'o', open_cb },
+	{ gettext("File/Insert File..."),	FL_CTRL + 'i', insert_cb },
+	{ gettext("File/Save File"),			FL_CTRL + 's', save_cb },
+	{ gettext("File/Save File As..._"),	FL_CTRL + FL_SHIFT + 's', saveas_cb, 0, FL_MENU_DIVIDER },
+/*TODO	{ gettext("Export"), 0, 0, 0, 	FL_SUBMENU },*/
+	{ gettext("File/New View"),		FL_ALT + 'w', view_cb },
+	{ gettext("File/Close View_"),	FL_CTRL + 'w', close_cb, 0, FL_MENU_DIVIDER },
+	{ gettext("File/Exit"),			FL_ALT + 'x', quit_cb },
+//		{ 0 },
+	{ gettext("Edit"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("Cut"),			FL_CTRL + 'x', cut_cb },
+		{ gettext("Copy"),			FL_CTRL + 'c', copy_cb },
+		{ gettext("Paste"),			FL_CTRL + 'v', paste_cb },
+		{ gettext("Delete"),		0, delete_cb, 0, FL_MENU_DIVIDER },
+		{ gettext("Insert"), 0, 0, 0, 	FL_SUBMENU },
+			{ gettext("options"),	FL_ALT + 'o', option_cb },
+			{ gettext("style"),		FL_ALT + 'i', style_cb },
+			{ gettext("filename"),	0, fname_cb },
+			{ gettext("command"),	FL_ALT + 'c', command_cb },
+			{ 0 },
+		{ gettext("Properties"),	0, settings_cb },
+		{ 0 },
+	{ gettext("Search"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("Find..."),		FL_CTRL + 'f', find_cb },
+		{ gettext("Find Again"),	FL_F + 3, find2_cb },
+		{ gettext("Replace..."),	FL_CTRL + 'r', replace_cb },
+		{ gettext("Replace Again"), FL_F + 4, replace2_cb },
+		{ 0 },
+/*TODO{ gettext("Graphics"), 0, 0, 0, FL_SUBMENU },*/
+/*TODO{ gettext("Data"), 0, 0, 0, FL_SUBMENU },*/
+	{ gettext("Help"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("MGL Help"),		FL_F + 1, help_cb },
+		{ gettext("MGL Examples"),	0, example_cb },
+		{ gettext("Hints and FAQ"),	0, hint_cb , 0, FL_MENU_INACTIVE},
+		{ gettext("About UDAV"),	0, about_cb },
+		{ 0 },
+	{ 0 }
+};
+//-----------------------------------------------------------------------------
+void mem_upd_cb(Fl_Widget *, void *v)
+{	((ScriptWindow*)v)->mem_init();	}
+//-----------------------------------------------------------------------------
+ScriptWindow *new_view()
+{
+	Fl_Tabs* tt;
+	Fl_Group *gg;
+	ScriptWindow *w = new ScriptWindow(930, 510, title);
+	w->begin();
+	w->menu = new Fl_Menu_Bar(0, 0, 930, 30);
+
+//	w->menu->add(gettext("File"), 0, 0, 0, FL_SUBMENU);	
+	w->menu->add(gettext("File/New File"), "", new_cb);
+	w->menu->add(gettext("File/Open File..."), "^o", open_cb, w);
+	w->menu->add(gettext("File/Insert File..."),	"^i", insert_cb, w);
+	w->menu->add(gettext("File/Save File"), "^s", save_cb, w);
+	w->menu->add(gettext("File/Save File As..."), 0, saveas_cb, w, FL_MENU_DIVIDER);
+	/*TODO	{ gettext("Export"), 0, 0, 0, 	FL_SUBMENU },*/
+	w->menu->add(gettext("File/New View"), "#w", view_cb, w);
+	w->menu->add(gettext("File/Close View"), "^w", close_cb, w, FL_MENU_DIVIDER);
+	w->menu->add(gettext("File/Exit"), "#x", quit_cb);
+//	w->menu->copy(menuitems, w);
+
+	Fl_Tile *t = new Fl_Tile(0,30,930,455);
+	tt = new Fl_Tabs(0,30,300,455,0);	tt->box(UDAV_UP_BOX);	w->ltab = tt;
+	gg = new Fl_Group(0,30,300,430);	gg->label(gettext("Script"));
+	add_editor(w);	gg->end();
+	tt->end();
+
+	tt = new Fl_Tabs(300,30,630,455,0);	tt->box(UDAV_UP_BOX);	w->rtab = tt;
+	gg = new Fl_Group(300,30,630,430,gettext("Canvas"));
+	w->graph = new Fl_MGL(300,30,630,430);	gg->end();
+	gg = new Fl_Group(300,30,630,430,gettext("Help"));
+	w->ghelp = gg;	add_help(w);	gg->end();
+	gg = new Fl_Group(300,30,630,430,gettext("Memory"));
+	add_mem(w);		gg->end();
+	tt->end();
+
+	w->status = new Fl_Box(0,485,930,25,"Ready");
+	w->status->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+	w->status->color(FL_BACKGROUND_COLOR);
+	w->status->box(FL_DOWN_BOX);
+	w->graph->status = w->status;
+
+	t->end();	w->end();	w->resizable(t);
+	tt->callback(mem_upd_cb, w);
+	w->callback((Fl_Callback *)close_cb, w);
+
+	num_windows++;
+	return w;
+}
+//-----------------------------------------------------------------------------
+void argument_set(int n, const char *s);
+int main(int argc, char **argv)
+{
+//	Fl::lock();
+	mgl_ask_func = mgl_ask_fltk;
+	char *buf, *buf2, ch;
+	pref.get("locale",buf,"ru_RU.cp1251");	setlocale(LC_CTYPE, buf);	free(buf);
+	pref.get("plastic_scheme",plastic_scheme,1);
+	pref.get("internal_font",internal_font,0);
+	pref.get("auto_exec",auto_exec,1);
+	pref.get("help_dir",docdir,MGL_DOC_DIR);	// docdir should be freed at exit
+
+	Fl::visual(FL_DOUBLE|FL_RGB);
+	if(plastic_scheme) Fl::scheme("gtk+");
+
+#ifdef USE_GETTEXT
+//	setlocale (LC_NUMERIC, "");
+//	bindtextdomain (PACKAGE, LOCALEDIR);
+//	textdomain (PACKAGE);
+#endif
+
+	textbuf = new Fl_Text_Buffer;
+	style_init();
+	ScriptWindow *w = new_view();
+
+	pref.get("font_dir",buf2,"");
+	pref.get("font_name",buf,"");
+	mgl_load_font(w->graph->FMGL->get_graph(),buf,buf2);
+	if(buf)	free(buf);
+	if(buf2)	free(buf2);
+
+	buf = 0;
+	while(1)
+	{
+		ch = getopt(argc, argv, "1:2:3:4:5:6:7:8:9:ho:L:");
+		if(ch>='1' && ch<='9')	argument_set(ch-'0', optarg);
+		else if(ch=='L')	setlocale(LC_CTYPE, optarg);
+		else if(ch=='h')
+		{
+			printf("mglconv convert mgl script to bitmap png file.\nCurrent version is 2.%g\n",MGL_VER2);
+			printf("Usage:\tmgllab [parameter(s)] scriptfile\n");
+			printf(	"\t-1 str       set str as argument $1 for script\n"
+					"\t...          ...\n"
+					"\t-9 str       set str as argument $9 for script\n"
+					"\t-L loc       set locale to loc\n"
+//					"\t-            get script from standard input\n"
+					"\t-h           print this message\n" );
+			free(docdir);	return 0;
+		}
+		// NOTE: I will not parse stdin here
+		else if(ch==-1 && optind<argc)	buf = argv[optind];
+		else if(ch==-1 && optind>=argc)	break;
+	}
+
+	w->show(1, argv);
+	if(buf && *buf && *buf!='-')
+	{
+		load_file(buf, -1);
+		if(auto_exec)	w->graph->update();
+	}
+	return Fl::run();
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/mathgl.cpp b/mgllab/mathgl.cpp
new file mode 100644
index 0000000..73e7964
--- /dev/null
+++ b/mgllab/mathgl.cpp
@@ -0,0 +1,139 @@
+/* mathgl.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "mgl2/mgl.h"
+#include "udav.h"
+//-----------------------------------------------------------------------------
+#include "xpm/alpha.xpm"
+#include "xpm/light.xpm"
+#include "xpm/alpha_on.xpm"
+#include "xpm/light_on.xpm"
+#include "xpm/zoom-fit-best.xpm"
+#include "xpm/zoom-fit-best-r.xpm"
+#include "xpm/film-r.xpm"
+#include "xpm/film-b.xpm"
+#include "xpm/media-seek-forward.xpm"
+#include "xpm/media-seek-backward.xpm"
+#include "xpm/go-previous.xpm"
+#include "xpm/go-next.xpm"
+#include "xpm/go-down.xpm"
+#include "xpm/zoom-out.xpm"
+#include "xpm/zoom-in.xpm"
+#include "xpm/go-up.xpm"
+#include "xpm/zoom-original.xpm"
+#include "xpm/view-refresh.xpm"
+#include "xpm/rotate.xpm"
+#include "xpm/rotate_on.xpm"
+#include "xpm/document-properties.xpm"
+//#include "xpm/preferences-system.xpm"
+#include "xpm/wire.xpm"
+//-----------------------------------------------------------------------------
+extern int internal_font;
+mglParse *Parse=0;
+//-----------------------------------------------------------------------------
+void udav_error(const char *Message, void *v)
+{	((Fl_MGL*)v)->status->label(Message);	}
+mreal udav_delay(void *v)
+{	return ((Fl_MGL*)v)->AnimDelay;	}
+void udav_reload(void *v)
+{	Parse->RestoreOnce();	((Fl_MGL*)v)->update();	}
+//-----------------------------------------------------------------------------
+void udav_next(void *v)	{	((Fl_MGL*)v)->next_frame();	}
+void Fl_MGL::next_frame()
+{
+	if(NArgs==0)
+	{
+		animate_cb(this,this);
+		if(NArgs==0)	return;
+	}
+	ArgCur = (ArgCur+1) % NArgs;
+	Parse->AddParam(0,Args[ArgCur]);
+	update();
+}
+//-----------------------------------------------------------------------------
+void udav_prev(void *v)	{	((Fl_MGL*)v)->prev_frame();	}
+void Fl_MGL::prev_frame()
+{
+	if(NArgs==0)
+	{
+		animate_cb(this,this);
+		if(NArgs==0)	return;
+	}
+	ArgCur = ArgCur>0 ? ArgCur-1 : NArgs-1;
+	Parse->AddParam(0,Args[ArgCur]);
+	update();
+}
+//-----------------------------------------------------------------------------
+Fl_MGL::Fl_MGL(int x, int y, int w, int h, const char *label) : Fl_MGLView(x,y,w,h,label)
+{
+	if(!Parse)	Parse = new mglParse;
+	Parse->AllowSetSize(true);
+	ArgBuf = 0;	NArgs = ArgCur = 0;
+	script = script_pre = 0;	par = this;
+	next = udav_next;	delay = udav_delay;
+	prev = udav_prev;	reload = udav_reload;
+/*#ifdef WIN32
+//	setlocale(LC_TYPE,"russian_Russia.CP1251");
+	char *path;
+	get_doc_dir(path);
+	if(!FMGL->GetFont()->Load("STIX",path && path[0] ? path : "."))	FMGL->GetFont()->Restore();
+	free(path);
+#endif*/
+}
+//-----------------------------------------------------------------------------
+Fl_MGL::~Fl_MGL()	{	clear_scripts();	if(ArgBuf)	delete []ArgBuf;	}
+//-----------------------------------------------------------------------------
+void Fl_MGL::clear_scripts()
+{
+	if(script)		free(script);
+	if(script_pre)	free(script_pre);
+}
+//-----------------------------------------------------------------------------
+void Fl_MGL::scripts(char *scr, char *pre)
+{	clear_scripts();	script=scr;	script_pre=pre;	}
+//-----------------------------------------------------------------------------
+int Fl_MGL::Draw(mglGraph *gr)
+{
+	Parse->Execute(gr,script_pre);
+	Parse->Execute(gr,script);
+	status->label(gr->Message());
+	return 0;
+}
+//-----------------------------------------------------------------------------
+void Fl_MGL::update()
+{
+	// NOTE: hint for old style View(). May be I should remove it!
+	if(!script || !strstr(script,"rotate"))	mgl_rotate(FMGL->get_graph(),0,0,0);
+
+	Fl_MGLView::update();
+
+	mglVar *v = Parse->FindVar("");
+	while(v)
+	{
+		if(v->o)	((TableWindow *)v->o)->update(v);
+		v = v->next;
+	}
+}
+//-----------------------------------------------------------------------------
+void add_suffix(char *fname, const char *ext)
+{
+	long n=strlen(fname);
+	if(n>4 && fname[n-4]=='.')
+	{	fname[n-3]=ext[0];	fname[n-2]=ext[1];	fname[n-1]=ext[2];	}
+	else	{	strcat(fname,".");	strcat(fname,ext);	}
+
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/option.cpp b/mgllab/option.cpp
new file mode 100644
index 0000000..e50323d
--- /dev/null
+++ b/mgllab/option.cpp
@@ -0,0 +1,439 @@
+/* option.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <string.h>
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Check_Button.H>
+#include <FL/Fl_Round_Button.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Return_Button.H>
+#include <FL/Fl_Tabs.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Choice.H>
+#include <FL/Fl_Spinner.H>
+#include <FL/Fl_Output.H>
+#include <FL/Fl_Text_Buffer.H>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+extern Fl_Menu_Item colors[];
+extern Fl_Text_Buffer	*textbuf;
+//-----------------------------------------------------------------------------
+struct OptionDlg
+{
+public:
+	Fl_Window* wnd;
+	int OK;
+	char result[256];
+	OptionDlg()	{	memset(this,0,sizeof(OptionDlg));	create_dlg();	}
+	~OptionDlg()	{	delete wnd;	}
+	void FillResult();
+protected:
+	Fl_Input *xmin, *xmax, *ymin, *ymax, *zmin, *zmax, *cmin, *cmax;
+	Fl_Input *alpha, *amb, *mesh, *font;
+	Fl_Choice *cut;
+
+	void create_dlg();
+} option_dlg;
+//-----------------------------------------------------------------------------
+struct StyleDlg
+{
+public:
+friend void style_set_cb(Fl_Widget *, void *v);
+friend void style_rdo_cb(Fl_Widget *, void *v);
+friend void font_cb(Fl_Widget *, void *v);
+friend void line_cb(Fl_Widget *, void *v);
+friend void face_cb(Fl_Widget *, void *v);
+	Fl_Window* wnd;
+	int OK;
+	char result[16];
+	StyleDlg()	{	memset(this,0,sizeof(StyleDlg));	create_dlg();	}
+	~StyleDlg()	{	delete wnd;	}
+protected:
+	Fl_Tabs *tab;
+	Fl_Group *ltab, *stab, *ftab;
+	Fl_Choice *cl, *cf, *c[7], *ae, *as;
+	Fl_Choice *dash, *mark, *dir, *text;
+	Fl_Spinner *lw;
+	Fl_Output *res;
+	Fl_Check_Button *d, *w, *sc, *rm, *it, *bf, *gt;
+	Fl_Round_Button *rl, *rc, *rr;
+
+	void create_dlg();
+} style_dlg;
+//-----------------------------------------------------------------------------
+void option_dlg_cb(Fl_Widget *, void *v)
+{	option_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void style_dlg_cb(Fl_Widget *, void *v)
+{	style_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void OptionDlg::create_dlg()
+{
+	Fl_Button *o;
+	wnd = new Fl_Window(490, 180, gettext("Command options"));
+	new Fl_Box(10, 15, 75, 25, gettext("X-Range"));
+	xmin = new Fl_Input(85, 15, 75, 25);
+	xmin->tooltip(gettext("Minimal value of X for cutting or for coordinate filling"));
+	xmax = new Fl_Input(165, 15, 75, 25);
+	xmax->tooltip(gettext("Maximal value of X for cutting or for coordinate filling"));
+	new Fl_Box(245, 15, 75, 25, gettext("Y-Range"));
+	ymin = new Fl_Input(320, 15, 75, 25);
+	ymin->tooltip(gettext("Minimal value of Y for cutting or for coordinate filling"));
+	ymax = new Fl_Input(400, 15, 75, 25);
+	ymax->tooltip(gettext("Maximal value of Y for cutting or for coordinate filling"));
+	new Fl_Box(10, 45, 75, 25, gettext("Z-Range"));
+	zmin = new Fl_Input(85, 45, 75, 25);
+	zmin->tooltip(gettext("Minimal value of Z for cutting or for coordinate filling"));
+	zmax = new Fl_Input(165, 45, 75, 25);
+	zmax->tooltip(gettext("Maximal value of Z for cutting or for coordinate filling"));
+	new Fl_Box(245, 45, 75, 25, gettext("C-Range"));
+	cmin = new Fl_Input(320, 45, 75, 25);
+	cmin->tooltip(gettext("Low border for determining color or alpha"));
+	cmax = new Fl_Input(400, 45, 75, 25);
+	cmax->tooltip(gettext("Upper border for determining color or alpha"));
+	{	Fl_Box *o = new Fl_Box(15, 75, 460, 5);			o->box(FL_UP_BOX);	}
+	alpha = new Fl_Input(25, 105, 75, 25, "Alpha");		alpha->align(FL_ALIGN_TOP);
+	alpha->tooltip(gettext("Alpha value (transparency) of surface or cloud"));
+	amb = new Fl_Input(110, 105, 75, 25, gettext("Ambient"));	amb->align(FL_ALIGN_TOP);
+	amb->tooltip(gettext("Own brightness of the surface"));
+	mesh = new Fl_Input(195, 105, 75, 25, gettext("Mesh Num"));	mesh->align(FL_ALIGN_TOP);
+	mesh->tooltip(gettext("Approximate number of mesh lines in plot"));
+	font = new Fl_Input(280, 105, 75, 25, gettext("Font Size"));	font->align(FL_ALIGN_TOP);
+	font->tooltip(gettext("Act as default value for font size"));
+	cut = new Fl_Choice(365, 105, 75, 25, gettext("Cutting"));	cut->align(FL_ALIGN_TOP);
+	cut->add(gettext("on"));	cut->add(gettext("off"));
+	cut->tooltip(gettext("Set cutting off/on for particular plot"));
+
+	o = new Fl_Button(320, 145, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(405, 145, 75, 25, gettext("OK"));	o->callback(option_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void OptionDlg::FillResult()
+{
+	double x1=0,y1=0,z1=0,x2=0,y2=0,z2=0;
+	bool u1,v1,w1,u2,v2,w2;
+	char str[64];
+	result[0]=0;
+
+	u1 = xmin->value()[0];	if(u1)	x1 = atof(xmin->value());
+	u2 = xmax->value()[0];	if(u2)	x2 = atof(xmax->value());
+	v1 = ymin->value()[0];	if(v1)	y1 = atof(ymin->value());
+	v2 = ymin->value()[0];	if(v2)	y2 = atof(ymax->value());
+	w1 = zmin->value()[0];	if(w1)	z1 = atof(zmin->value());
+	w2 = zmin->value()[0];	if(w2)	z2 = atof(zmax->value());
+	if(u1 && u2)	{snprintf(str,64,"; xrange %g %g",x1,x2);	strcat(result,str);}
+	if(v1 && v2)	{snprintf(str,64,"; yrange %g %g",y1,y2);	strcat(result,str);}
+	if(w1 && w2)	{snprintf(str,64,"; zrange %g %g",z1,z2);	strcat(result,str);}
+
+	u1 = cmin->value()[0];	if(u1)	x1 = atof(cmin->value());
+	u2 = cmax->value()[0];	if(u2)	x2 = atof(cmax->value());
+	if(u1&&u2)	{snprintf(str,64,"; crange %g %g",x1,x2);	strcat(result,str);}
+
+	if(alpha->value()[0])
+	{	snprintf(str,64,"; alpha %g",atof(alpha->value()));	strcat(result,str);}
+	if(amb->value()[0])
+	{	snprintf(str,64,"; ambient %g",atof(amb->value()));	strcat(result,str);}
+	if(mesh->value()[0])
+	{	snprintf(str,64,"; meshnum %g",atof(mesh->value()));strcat(result,str);}
+	if(font->value()[0])
+	{	snprintf(str,64,"; fontsize '%g'",atof(font->value()));	strcat(result,str);}
+	if(cut->value()>=0)
+	{snprintf(str,64,"; cut %s",cut->value()==0?"on":"off");	strcat(result,str);}
+}
+//-----------------------------------------------------------------------------
+void option_cb(Fl_Widget *, void *v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	OptionDlg *s = &option_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// insert at the end of string
+	{
+		long i=e->editor->insert_position(), j=textbuf->line_end(i);
+		s->FillResult();
+		e->editor->insert_position(j);
+		e->editor->insert(s->result);
+	}
+}
+//-----------------------------------------------------------------------------
+void option_in_cb(Fl_Widget *, void *v)
+{
+	Fl_Input* e = (Fl_Input*)v;
+	OptionDlg *s = &option_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)
+	{
+		s->FillResult();
+		e->value(s->result);
+	}
+}
+//-----------------------------------------------------------------------------
+Fl_Menu_Item arrows[] = {
+	{("none")},	//_
+	{("arrow")},	//A
+	{("back arrow")},	//V
+	{("stop")},	//I
+	{("size")},	//K
+	{("triangle")},	//T
+	{("square")},	//S
+	{("rhomb")},	//D
+	{("circle")},	//O
+	{0}};
+//-----------------------------------------------------------------------------
+Fl_Menu_Item dashing[] = {
+	{("solid")},	//-
+	{("dash")},	//|
+	{("dash dot")},	//j
+	{("small dash")},	//;
+	{("small dash dot")},	//i
+	{("dots")},	//:
+	{("none")},	//
+	{0}};
+//-----------------------------------------------------------------------------
+Fl_Menu_Item markers[] = {
+	{("none")},	//
+	{("circle")},	//o
+	{("cross")},	//+
+	{("skew cross")},	//x
+	{("square")},	//s
+	{("rhomb")},	//d
+	{("point")},	//.
+	{("triangle up")},	//^
+	{("triangle down")},	//v
+	{0}};
+//-----------------------------------------------------------------------------
+void style_set_cb(Fl_Widget *,void *)
+{
+	StyleDlg  *s = &style_dlg;
+	Fl_Widget *t = s->tab->value();
+	const char *cols = " wbgrcmylenuqphkWBGRCMYLENUQPH";
+	char *r = s->result;
+	long i=0,j;
+	if(t==s->ltab)	// line style
+	{
+		const char *aa = "_AVIKTSDO", *dd="-|j;i: ", *mm="#o+xsd.^v";
+		if(s->cl->value()>0)	r[i++]=cols[s->cl->value()];
+		if(s->dash->value()>0)	r[i++]=dd[s->dash->value()];
+		if(s->mark->value()>0)	r[i++]=mm[s->mark->value()];
+		if(s->lw->value()>1 || s->lw->value()==0)
+			r[i++] = '0'+int(0.1+s->lw->value());
+		if(s->as->value()>0)
+		{	r[i++]=aa[s->ae->value()];	r[i++]=aa[s->as->value()];	}
+		else if(s->ae->value()>0)	r[i++]=aa[s->ae->value()];
+	}
+	else if(t==s->stab)	// surf style
+	{
+		for(j=0;j<7;j++)
+		{
+			if(s->c[j]->value()>0)	r[i++]=cols[s->c[j]->value()];
+			else break;
+		}
+		if(s->d->value())	r[i++] = 'd';
+		if(s->w->value())	r[i++] = '#';
+		if(s->dir->value()>=0)	r[i++] = 'x'+s->dir->value();
+		if(s->text->value()>0)	r[i++] = s->text->value()==1 ? 't':'T';
+	}
+	else if(t==s->ftab)	// text style
+	{
+		if(s->rm->value())	r[i++] = 'r';
+		if(s->sc->value())	r[i++] = 's';
+		if(s->it->value())	r[i++] = 'i';
+		if(s->bf->value())	r[i++] = 'b';
+		if(s->gt->value() && !s->rm->value())	r[i++] = 'g';
+		if(s->rl->value())	r[i++] = 'L';
+		else if(s->rc->value())	r[i++] = 'C';
+		else if(s->rr->value())	r[i++] = 'R';
+		if(s->cf->value()>0)
+		{	r[i++]=':';	r[i++]=cols[s->cf->value()];	}
+	}
+	r[i]=0;
+	s->res->value(r);
+}
+//-----------------------------------------------------------------------------
+void style_rdo_cb(Fl_Widget *,void *v)
+{
+	StyleDlg  *s = &style_dlg;
+	s->rl->value(0);	s->rc->value(0);	s->rr->value(0);
+	((Fl_Round_Button *)v)->value(1);
+	style_set_cb(0,0);
+}
+//-----------------------------------------------------------------------------
+void StyleDlg::create_dlg()
+{
+	wnd = new Fl_Window(295, 337, gettext("String with line/surf/text style"));
+	tab = new Fl_Tabs(0, 0, 295, 255);	tab->callback(style_set_cb);
+	tab->box(UDAV_UP_BOX);
+
+	ltab = new Fl_Group(0, 25, 295, 230, gettext("Line style"));
+	as = new Fl_Choice(10, 50, 80, 25, gettext("Arrow at start"));
+	as->align(FL_ALIGN_TOP);	as->copy(arrows);	as->callback(style_set_cb);
+//	as->tooltip(gettext("Type of arrow at first point of line or curve"));
+	dash = new Fl_Choice(110, 50, 80, 25, gettext("Dashing"));
+	dash->align(FL_ALIGN_TOP);	dash->copy(dashing);dash->callback(style_set_cb);
+//	dash->tooltip(gettext("Type dashing for line or curve"));
+	ae = new Fl_Choice(210, 50, 80, 25, gettext("Arrow at end"));
+	ae->align(FL_ALIGN_TOP);	ae->copy(arrows);	ae->callback(style_set_cb);
+//	ae->tooltip(gettext("Type of arrow at last point of line or curve"));
+	cl = new Fl_Choice(110, 85, 80, 25, gettext("Color"));	cl->copy(colors);
+	cl->callback(style_set_cb);
+	mark = new Fl_Choice(110, 120, 80, 25, gettext("Marks"));
+	mark->copy(markers);	mark->callback(style_set_cb);
+//	mark->tooltip(gettext("Type of marks at positions of data points"));
+	lw = new Fl_Spinner(110, 155, 80, 25, gettext("Line width"));
+	lw->range(0,9);	lw->step(1);	lw->callback(style_set_cb);
+//	lw->tooltip(gettext("Relative width of line or curve"));
+	ltab->end();
+
+	stab = new Fl_Group(0, 25, 295, 230, gettext("Color scheme"));	stab->hide();
+	c[0] = new Fl_Choice(15, 45, 75, 25, gettext("Color order"));
+	c[0]->align(FL_ALIGN_TOP);	c[0]->copy(colors);	c[0]->callback(style_set_cb);
+	c[1] = new Fl_Choice(15, 75, 75, 25);	c[1]->copy(colors);	c[1]->callback(style_set_cb);
+	c[2] = new Fl_Choice(15, 105, 75, 25);	c[2]->copy(colors);	c[2]->callback(style_set_cb);
+	c[3] = new Fl_Choice(15, 135, 75, 25);	c[3]->copy(colors);	c[3]->callback(style_set_cb);
+	c[4] = new Fl_Choice(15, 165, 75, 25);	c[4]->copy(colors);	c[4]->callback(style_set_cb);
+	c[5] = new Fl_Choice(15, 195, 75, 25);	c[5]->copy(colors);	c[5]->callback(style_set_cb);
+	c[6] = new Fl_Choice(15, 225, 75, 25);	c[6]->copy(colors);	c[6]->callback(style_set_cb);
+	d = new Fl_Check_Button(100, 45, 180, 25, gettext("Colors along coordinates"));
+//	w->tooltip(gettext("Set face color proportional to its position"));
+	d->callback(style_set_cb);
+	w = new Fl_Check_Button(100, 75, 180, 25, gettext("Wire or mesh plot"));
+	w->callback(style_set_cb);
+//	w->tooltip(gettext("Switch to draw wire isosurface or set to draw mesh on surface"));
+	dir = new Fl_Choice(210, 105, 75, 25, gettext("Axial direction"));
+	dir->add("x");	dir->add("y");	dir->add("z");	dir->callback(style_set_cb);
+	text = new Fl_Choice(210, 135, 75, 25, gettext("Text on contours"));
+//	text->tooltip("Draw contour values near contour lines"));
+	text->add(gettext("none"));	text->add(gettext("under"));
+	text->add(gettext("above"));	text->callback(style_set_cb);
+	stab->end();
+
+	ftab = new Fl_Group(0, 25, 295, 230, gettext("Text style"));		ftab->hide();
+	sc = new Fl_Check_Button(15, 40, 120, 25, gettext("Script font/style"));	sc->callback(style_set_cb);
+	rm = new Fl_Check_Button(15, 70, 120, 25, gettext("Roman font"));	rm->callback(style_set_cb);
+	gt = new Fl_Check_Button(15, 100, 120, 25, gettext("Gothic font"));	gt->callback(style_set_cb);
+	it = new Fl_Check_Button(15, 130, 120, 25, gettext("Italic style"));	it->callback(style_set_cb);
+	bf = new Fl_Check_Button(15, 160, 120, 25, gettext("Bold style"));	bf->callback(style_set_cb);
+	cf = new Fl_Choice(200, 40, 80, 25, gettext("Text color"));	cf->copy(colors);cf->callback(style_set_cb);
+	{ Fl_Box* o = new Fl_Box(160, 90, 120, 90, gettext("Alignment"));
+		o->box(FL_DOWN_BOX);	o->align(FL_ALIGN_TOP);}
+	rl = new Fl_Round_Button(170, 100, 100, 25, gettext("left"));		rl->callback(style_rdo_cb,rl);
+	rc = new Fl_Round_Button(170, 125, 100, 25, gettext("at center"));	rc->callback(style_rdo_cb,rc);
+	rr = new Fl_Round_Button(170, 150, 100, 25, gettext("right"));		rr->callback(style_rdo_cb,rr);
+	ftab->end();
+
+	tab->end();
+	res = new Fl_Output(50, 265, 235, 25, gettext("Result"));
+//	res->tooltip(gettext("Resulting string which will be used as argument of a command"));
+	Fl_Button *o;
+	o = new Fl_Button(125, 300, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(210, 300, 75, 25, gettext("OK"));	o->callback(style_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void style_cb(Fl_Widget *, void *v)
+{
+	ScriptWindow* e = (ScriptWindow*)v;
+	StyleDlg *s = &style_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// replace current selection
+	{
+		int p,q;
+		char str[20];
+		snprintf(str,20,"'%s'",s->result);
+		textbuf->selection_position(&p, &q);
+		if(p==q)	e->editor->insert(str);
+		else		textbuf->replace_selection(str);
+	}
+}
+//-----------------------------------------------------------------------------
+void style_in_cb(Fl_Widget *, void *v)
+{
+	Fl_Input* e = (Fl_Input*)v;
+	StyleDlg *s = &style_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	e->value(s->result);
+}
+//-----------------------------------------------------------------------------
+void font_cb(Fl_Widget *, void *v)
+{
+	Fl_Input* e = (Fl_Input *)v;
+	StyleDlg *s = &style_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->tab->value(s->ftab);
+	s->ltab->deactivate();
+	s->stab->deactivate();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// replace current selection
+		e->value(s->result);
+	s->ltab->activate();
+	s->stab->activate();
+}
+//-----------------------------------------------------------------------------
+void line_cb(Fl_Widget *, void *v)
+{
+	Fl_Input* e = (Fl_Input *)v;
+	StyleDlg *s = &style_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->tab->value(s->ltab);
+	s->ftab->deactivate();
+	s->stab->deactivate();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// replace current selection
+		e->value(s->result);
+	s->ftab->activate();
+	s->stab->activate();
+}
+//-----------------------------------------------------------------------------
+void face_cb(Fl_Widget *, void *v)
+{
+	Fl_Input* e = (Fl_Input *)v;
+	StyleDlg *s = &style_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->tab->value(s->stab);
+	s->ltab->deactivate();
+	s->ftab->deactivate();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	// replace current selection
+		e->value(s->result);
+	s->ltab->activate();
+	s->ftab->activate();
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/setup.cpp b/mgllab/setup.cpp
new file mode 100644
index 0000000..24e3e1c
--- /dev/null
+++ b/mgllab/setup.cpp
@@ -0,0 +1,422 @@
+/* setup.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "mgl2/mgl.h"
+#include <string.h>
+#include <FL/Fl_Tabs.H>
+#include <FL/Fl_Round_Button.H>
+#include <FL/Fl_Multiline_Input.H>
+#include <FL/Fl_Spinner.H>
+#include <locale.h>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+extern int auto_exec, plastic_scheme, internal_font;
+//-----------------------------------------------------------------------------
+void setup_dlg_cb(Fl_Widget *, void *v)
+{	SetupDlg *s = (SetupDlg *)v;	s->OK = true;	s->wnd->hide();	}
+//-----------------------------------------------------------------------------
+void setup_sav_cb(Fl_Widget *, void *v)
+{
+	SetupDlg *e = (SetupDlg *)v;
+	char *buf = e->ToScript();
+	const char *fname;
+	if(buf[0])
+	{
+		fname = e->templ->value();
+		if(fname[0]==0)	fname = "template.mgl";
+		FILE *fp = fopen(fname,"w");
+		fputs(buf,fp);
+		fclose(fp);
+	}
+	free(buf);
+}
+//-----------------------------------------------------------------------------
+void close_dlg_cb(Fl_Widget *, void *v)
+{	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void SetupDlg::CreateGen()
+{
+	xmin = new Fl_Input(105, 50, 75, 25);	xmax = new Fl_Input(105, 80, 75, 25);
+	ymin = new Fl_Input(190, 50, 75, 25);	ymax = new Fl_Input(190, 80, 75, 25);
+	zmin = new Fl_Input(275, 50, 75, 25);	zmax = new Fl_Input(275, 80, 75, 25);
+	cmin = new Fl_Input(360, 50, 75, 25);	cmax = new Fl_Input(360, 80, 75, 25);
+
+	xorg = new Fl_Input(105, 110, 75, 25);
+	yorg = new Fl_Input(190, 110, 75, 25);
+	zorg = new Fl_Input(275, 110, 75, 25);
+	xlab = new Fl_Input(105, 140, 75, 25);
+	ylab = new Fl_Input(190, 140, 75, 25);
+	zlab = new Fl_Input(275, 140, 75, 25);
+
+	xpos = new Fl_Choice(105, 170, 75, 25);	xpos->add(gettext("at minumum"));
+		xpos->add("at center");	xpos->add(gettext("at maxumum"));	xpos->value(1);
+	ypos = new Fl_Choice(190, 170, 75, 25);	ypos->add(gettext("at minumum"));
+		ypos->add("at center");	ypos->add(gettext("at maxumum"));	ypos->value(1);
+	zpos = new Fl_Choice(275, 170, 75, 25);	zpos->add(gettext("at minumum"));
+		zpos->add("at center");	zpos->add(gettext("at maxumum"));	zpos->value(1);
+	xtik = new Fl_Input(105, 200, 75, 25);
+	ytik = new Fl_Input(190, 200, 75, 25);
+	ztik = new Fl_Input(275, 200, 75, 25);
+	xsub = new Fl_Input(105, 230, 75, 25);
+	ysub = new Fl_Input(190, 230, 75, 25);
+	zsub = new Fl_Input(275, 230, 75, 25);
+
+	{ Fl_Box* o = new Fl_Box(10, 260, 470, 5);	o->box(FL_DOWN_BOX);	}
+	alphad = new Fl_Input(20, 285, 75, 25, gettext("AlphaDef"));	alphad->align(FL_ALIGN_TOP);
+	ambient = new Fl_Input(105, 285, 75, 25, gettext("Ambient"));	ambient->align(FL_ALIGN_TOP);
+	basew = new Fl_Input(190, 285, 75, 25, gettext("Base Width"));	basew->align(FL_ALIGN_TOP);
+	mesh = new Fl_Input(275, 285, 75, 25, gettext("Mesh Num"));		mesh->align(FL_ALIGN_TOP);
+	axial = new Fl_Choice(360, 285, 75, 25, gettext("Axial Dir"));	axial->align(FL_ALIGN_TOP);
+	axial->add("x");	axial->add("y");	axial->add("z");
+	font = new Fl_Input(20, 330, 50, 25, gettext("Font"));			font->align(FL_ALIGN_TOP);
+	{ Fl_Button* o = new Fl_Button(70, 330, 25, 25, "..");	o->callback(font_cb, font);
+		o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);	}
+	size = new Fl_Input(105, 330, 75, 25, gettext("Font Size"));	size->align(FL_ALIGN_TOP);
+	alpha = new Fl_Check_Button(190, 330, 75, 25, gettext("Alpha on"));
+	light = new Fl_Check_Button(275, 330, 75, 25, gettext("Light on"));
+	rotate = new Fl_Check_Button(360, 330, 90, 25, gettext("Rotate text"));
+}
+//-----------------------------------------------------------------------------
+void SetupDlg::CreateLid()
+{
+	const char *str[10]={"0:", "1:", "2:", "3:", "4:", "5:", "6:", "7:", "8:", "9:"};
+	int h;
+	for(long i=0;i<10;i++)
+	{
+		h = 55 + 30*i;
+		new Fl_Box(10, h, 25, 25, str[i]);
+		lid[i] = new Fl_Check_Button(35, h, 40, 25, gettext("on"));
+		xid[i] = new Fl_Input(85, h, 75, 25);
+		yid[i] = new Fl_Input(165, h, 75, 25);
+		zid[i] = new Fl_Input(245, h, 75, 25);
+		cid[i] = new Fl_Choice(325, h, 75, 25);
+		cid[i]->copy(colors);
+		bid[i] = new Fl_Input(405, h, 75, 25);
+	}
+}
+//-----------------------------------------------------------------------------
+void SetupDlg::CreateDlg()
+{
+	OK = false;
+	wnd = new Fl_Window(490, 406, gettext("Setup graphics"));
+	Fl_Tabs* t = new Fl_Tabs(0, 0, 490, 360);	t->box(UDAV_UP_BOX);
+
+	Fl_Group *g = new Fl_Group(0, 25, 485, 330, gettext("General"));
+	new Fl_Box(105, 30, 75, 20, gettext("X axis"));
+	new Fl_Box(190, 30, 75, 20, gettext("Y axis"));
+	new Fl_Box(275, 30, 75, 20, gettext("Z axis"));
+	new Fl_Box(360, 30, 75, 20, gettext("Color"));
+
+	new Fl_Box(25, 50, 75, 25, gettext("Minimal"));
+	new Fl_Box(25, 80, 75, 25, gettext("Maximal"));
+	new Fl_Box(25, 110, 75, 25, gettext("Origin"));
+	new Fl_Box(25, 140, 75, 25, gettext("Label"));
+	new Fl_Box(25, 170, 75, 25, gettext("Position"));
+	new Fl_Box(25, 200, 75, 25, gettext("Ticks"));
+	new Fl_Box(25, 230, 75, 25, gettext("SubTicks"));
+	CreateGen();
+	g->end();
+
+	g = new Fl_Group(0, 25, 485, 330, gettext("Light"));	g->hide();
+	new Fl_Box(10, 30, 25, 25, gettext("ID"));
+	new Fl_Box(40, 30, 40, 25, gettext("State"));
+	new Fl_Box(85, 30, 75, 25, gettext("X position"));
+	new Fl_Box(165, 30, 75, 25, gettext("Y position"));
+	new Fl_Box(245, 30, 75, 25, gettext("Z position"));
+	new Fl_Box(325, 30, 75, 25, gettext("Color"));
+	new Fl_Box(405, 30, 75, 25, gettext("Brightness"));
+	CreateLid();
+	g->end();
+
+	g = new Fl_Group(0, 25, 485, 330, gettext("Setup code"));	g->hide();
+	code = new Fl_Help_View(0, 25, 485, 330);
+	g->end();
+
+	t->end();	//Fl_Group::current()->resizable(t);
+	Fl_Button *o;
+	templ = new Fl_Input(120, 370, 110, 25, gettext("Template name"));
+	templ->value("template.mgl");
+	o = new Fl_Button(230, 370, 80, 25, gettext("Save"));	o->callback(setup_sav_cb, wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Save settings to file template.mgl.\nYou may use it later by 'call template.mgl'"));
+
+	o = new Fl_Button(315, 370, 80, 25, gettext("Cancel"));	o->callback(close_dlg_cb, wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(400, 370, 80, 25, gettext("OK"));	o->callback(setup_dlg_cb, this);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+char *SetupDlg::ToScript()
+{
+	long i;
+	double x1=0,y1=0,z1=0,x2=0,y2=0,z2=0;
+	bool u1,v1,w1,u2,v2,w2;
+	const char *cols = " wbgrcmylenuqphkWBGRCMYLENUQPH";
+	char *buf = (char *)malloc(1024*sizeof(char)), str[128];
+	long num = 1024, cur = 0;
+	buf[0]=0;
+	if(!OK)	return buf;
+
+	for(i=0;i<10;i++)	// set light sources
+	{
+//fl_message("before %d lid:%d xid:%s yid:%s zid:%s",i,lid[i]->value(), xid[i]->value(),yid[i]->value(),zid[i]->value());
+		if(!lid[i]->value())	continue;
+		if(!xid[i]->value()[0] || !yid[i]->value()[0] || !zid[i]->value()[0])	continue;
+		x1=atof(xid[i]->value());	y1=atof(yid[i]->value());	z1=atof(zid[i]->value());
+		if(!bid[i]->value()[0])
+			cur += snprintf(str,128,"light %ld %g %g %g '%c'\n",i,x1,y1,z1,
+						cols[cid[i]->value()]);
+		else
+			cur += snprintf(str,128,"light %ld %g %g %g '%c' %g\n",i,x1,y1,z1,
+						cols[cid[i]->value()],atof(bid[i]->value()));
+		strcat(buf,str);
+	}
+	u1 = xmin->value()[0];	if(u1)	x1 = atof(xmin->value());
+	u2 = xmax->value()[0];	if(u2)	x2 = atof(xmax->value());
+	v1 = ymin->value()[0];	if(v1)	y1 = atof(ymin->value());
+	v2 = ymin->value()[0];	if(v2)	y2 = atof(ymax->value());
+	w1 = zmin->value()[0];	if(w1)	z1 = atof(zmin->value());
+	w2 = zmin->value()[0];	if(w2)	z2 = atof(zmax->value());
+	if(u1&&v1&&w1&&u2&&v2&&w2)
+	{
+		cur+=snprintf(str,128,"axis %g %g %g %g %g %g\n",x1,y1,z1,x2,y2,z2);
+		strcat(buf,str);
+	}
+	else
+	{
+		if(u1 && u2)	{cur+=snprintf(str,128,"xrange %g %g\n",x1,x2);	strcat(buf,str);}
+		if(v1 && v2)	{cur+=snprintf(str,128,"yrange %g %g\n",y1,y2);	strcat(buf,str);}
+		if(w1 && w2)	{cur+=snprintf(str,128,"zrange %g %g\n",z1,z2);	strcat(buf,str);}
+	}
+	u1 = cmin->value()[0];	if(u1)	x1 = atof(cmin->value());
+	u2 = cmax->value()[0];	if(u2)	x2 = atof(cmax->value());
+	if(u1&&u2)	{cur+=snprintf(str,128,"crange %g %g\n",x1,x2);	strcat(buf,str);}
+	if(cur>num-256)	{	num+=512;	buf = (char *)realloc(buf,num*sizeof(char));	}
+
+	u1 = xorg->value()[0];	if(u1)	x1 = atof(xorg->value());
+	v1 = yorg->value()[0];	if(v1)	y1 = atof(yorg->value());
+	w1 = zorg->value()[0];	if(w1)	z1 = atof(zorg->value());
+	if(u1&&v1&&w1)	{snprintf(str,128,"origin %g %g %g\n",x1,y1,z1);	strcat(buf,str);}
+
+	u1 = xtik->value()[0];	if(u1)	x1 = atof(xtik->value());
+	u2 = xsub->value()[0];	if(u2)	x2 = atoi(xsub->value());
+	v1 = ytik->value()[0];	if(v1)	y1 = atof(ytik->value());
+	v2 = ysub->value()[0];	if(v2)	y2 = atoi(ysub->value());
+	w1 = ztik->value()[0];	if(w1)	z1 = atof(ztik->value());
+	w2 = zsub->value()[0];	if(w2)	z2 = atoi(zsub->value());
+	if(u1 && u2)	{cur+=snprintf(str,128,"xtick %g %g\n",x1,x2);	strcat(buf,str);}
+	if(v1 && v2)	{cur+=snprintf(str,128,"ytick %g %g\n",y1,y2);	strcat(buf,str);}
+	if(w1 && w2)	{cur+=snprintf(str,128,"ztick %g %g\n",z1,z2);	strcat(buf,str);}
+	if(u1 && !u2)	{cur+=snprintf(str,128,"xtick %g\n",x1);	strcat(buf,str);}
+	if(v1 && !v2)	{cur+=snprintf(str,128,"ytick %g\n",y1);	strcat(buf,str);}
+	if(w1 && !w2)	{cur+=snprintf(str,128,"ztick %g\n",z1);	strcat(buf,str);}
+
+	if(xlab->value()[0])
+	{
+		cur+=snprintf(str,128,"xlabel '%s' %d\n",xlab->value(), xpos->value()-1);
+		strcat(buf,str);
+	}
+	if(ylab->value()[0])
+	{
+		cur+=snprintf(str,128,"ylabel '%s' %d\n",ylab->value(), ypos->value()-1);
+		strcat(buf,str);
+	}
+	if(zlab->value()[0])
+	{
+		cur+=snprintf(str,128,"zlabel '%s' %d\n",zlab->value(), zpos->value()-1);
+		strcat(buf,str);
+	}
+	if(alphad->value()[0])
+	{
+		cur+=snprintf(str,128,"alphadef %g\n",atof(alphad->value()));
+		strcat(buf,str);
+	}
+	if(ambient->value()[0])
+	{
+		cur+=snprintf(str,128,"ambient %g\n",atof(ambient->value()));
+		strcat(buf,str);
+	}
+
+	if(basew->value()[0])
+	{
+		cur+=snprintf(str,128,"baselinewidth %g\n",atof(basew->value()));
+		strcat(buf,str);
+	}
+	if(mesh->value()[0])
+	{
+		cur+=snprintf(str,128,"meshnum %g\n",atof(mesh->value()));
+		strcat(buf,str);
+	}
+	if(axial->value()>=0)
+	{
+		cur+=snprintf(str,128,"axialdir '%c'\n",'x'+axial->value());
+		strcat(buf,str);
+	}
+
+	if(font->value()[0])
+	{
+		cur+=snprintf(str,128,"font '%s'",font->value());
+		strcat(buf,str);
+		if(size->value())	cur+=snprintf(str,128," %g\n",atof(size->value()));
+		else	cur+=snprintf(str,128,"\n");
+		strcat(buf,str);
+	}
+	if(rotate->value())	{cur+=snprintf(str,128,"rotatetext on\n");	strcat(buf,str);}
+
+	if(alpha->value())	{cur+=snprintf(str,128,"alpha on\n");	strcat(buf,str);}
+	if(light->value())	{cur+=snprintf(str,128,"light on\n");	strcat(buf,str);}
+
+	code->value(buf);
+	return buf;
+}
+//-----------------------------------------------------------------------------
+void setup_cb(Fl_Widget *, void *d)
+{
+	if(d==0)	return;
+	SetupDlg *s = ((ScriptWindow *)d)->setup_dlg;
+	s->OK = false;
+	s->wnd->set_modal();
+	s->wnd->show();
+	while(s->wnd->shown())	Fl::wait();
+	if(s->OK)	((ScriptWindow *)d)->graph->update();
+}
+//-----------------------------------------------------------------------------
+Fl_Menu_Item colors[] = {
+	{"-----", 0,0,0,0,0,0,0, 0},	//
+	{("white"), 0,0,0,0,0,0,0, fl_rgb_color(0,0,0)},	//w
+	{("blue"), 0,0,0,0,0,0,0, fl_rgb_color(0,0,255)},			//b
+	{("lime"), 0,0,0,0,0,0,0, fl_rgb_color(0,255,0)},			//g
+	{("red"), 0,0,0,0,0,0,0, fl_rgb_color(255,0,0)},			//r
+	{("cyan"), 0,0,0,0,0,0,0, fl_rgb_color(0,255,255)},		//c
+	{("magenta"), 0,0,0,0,0,0,0, fl_rgb_color(255,0,255)},	//m
+	{("yellow"), 0,0,0,0,0,0,0, fl_rgb_color(255,255,0)},		//y
+	{("springgreen"), 0,0,0,0,0,0,0, fl_rgb_color(0,255,127)},//l
+	{("lawngreen"), 0,0,0,0,0,0,0, fl_rgb_color(127,255,0)},	//e
+	{("skyblue"), 0,0,0,0,0,0,0, fl_rgb_color(0,127,255)},	//n
+	{("blueviolet"), 0,0,0,0,0,0,0, fl_rgb_color(127,0,255)},	//u
+	{("orange"), 0,0,0,0,0,0,0, fl_rgb_color(255,127,0)},		//q
+	{("deeppink"), 0,0,0,0,0,0,0, fl_rgb_color(255,0,127)},	//p
+	{("gray"), 0,0,0,0,0,0,0, fl_rgb_color(127,127,127)},		//h
+
+	{("black"), 0,0,0,0,0,0,0, fl_rgb_color(0,0,0)},	//k
+	{("lightgray"), 0,0,0,0,0,0,0, fl_rgb_color(179,179,179)},	//W
+	{("navy"), 0,0,0,0,0,0,0, fl_rgb_color(0,0,127)},	//B
+	{("green"), 0,0,0,0,0,0,0, fl_rgb_color(0,127,0)},	//G
+	{("maroon"), 0,0,0,0,0,0,0, fl_rgb_color(127,0,0)},	//R
+	{("teal"), 0,0,0,0,0,0,0, fl_rgb_color(0,127,127)},	//C
+	{("purple"), 0,0,0,0,0,0,0, fl_rgb_color(127,0,127)},	//M
+	{("olive"), 0,0,0,0,0,0,0, fl_rgb_color(127,127,0)},	//Y
+	{("seagreen"), 0,0,0,0,0,0,0, fl_rgb_color(0,127,77)},	//L
+	{("darklawn"), 0,0,0,0,0,0,0, fl_rgb_color(77,127,0)},	//E
+	{("darkskyblue"), 0,0,0,0,0,0,0, fl_rgb_color(0,77,127)},	//N
+	{("indigo"), 0,0,0,0,0,0,0, fl_rgb_color(77,0,127)},	//U
+	{("brown"), 0,0,0,0,0,0,0, fl_rgb_color(127,77,0)},	//Q
+	{("darkpink"), 0,0,0,0,0,0,0, fl_rgb_color(127,0,77)},	//P
+	{("darkgray"), 0,0,0,0,0,0,0, fl_rgb_color(77,77,77)},	//H
+{0, 0,0,0,0,0,0,0, 0}};
+//-----------------------------------------------------------------------------
+struct PropDlg
+{
+	Fl_Window *wnd;
+	Fl_MGL *graph;
+
+	Fl_Input *path, *locale, *font, *fpath;
+	Fl_Check_Button *plast, *aexec, *ifont;
+	PropDlg()	{	memset(this,0,sizeof(PropDlg));	create_dlg();	}
+	~PropDlg()	{	delete wnd;	}
+	void create_dlg();
+	void finish();
+	void init();
+} prop_dlg;
+//-----------------------------------------------------------------------------
+void PropDlg::init()
+{
+	int a, p;
+	char *buf;
+	pref.get("plastic_scheme",p,1);	plast->value(p);
+	pref.get("auto_exec",a,1);		aexec->value(a);
+	pref.get("internal_font",a,0);	ifont->value(a);
+	path->value(docdir);
+	pref.get("font_dir",buf,"");	fpath->value(buf);	free(buf);
+	pref.get("font_name",buf,"");	font->value(buf);	free(buf);
+	pref.get("locale",buf,"ru_RU.cp1251");	locale->value(buf);	free(buf);
+}
+//-----------------------------------------------------------------------------
+void PropDlg::finish()
+{
+	int a, p;
+	p = plast->value();
+	if(p!=plastic_scheme)
+	{
+		plastic_scheme = p;
+		pref.set("plastic_scheme",p);
+		Fl::scheme(p?"plastic":"none");
+	}
+	a = aexec->value();
+	if(a!=auto_exec)
+	{
+		auto_exec = a;
+		pref.set("auto_exec",a);
+	}
+	a = ifont->value();
+	if(a!=internal_font)
+	{
+		internal_font = a;
+		pref.set("internal_font",a);
+	}
+	if(path->value()[0])	pref.set("help_dir",path->value());
+	if(locale->value()[0])
+	{
+		pref.set("locale", locale->value());
+		setlocale(LC_CTYPE, locale->value());
+	}
+	pref.set("font_dir",fpath->value());
+	pref.set("font_name",font->value());
+	if(graph)	mgl_load_font(graph->FMGL->get_graph(), font->value(), fpath->value());
+}
+//-----------------------------------------------------------------------------
+void prop_dlg_cb(Fl_Widget *, void *v)
+{	prop_dlg.finish();	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void PropDlg::create_dlg()
+{
+	wnd = new Fl_Double_Window(320, 300, gettext("UDAV settings"));
+	path = new Fl_Input(10, 25, 305, 25, gettext("Path for help files"));	path->align(FL_ALIGN_TOP_LEFT);
+
+	font = new Fl_Input(10, 75, 305, 25, gettext("Font typeface"));	font->align(FL_ALIGN_TOP_LEFT);
+	fpath = new Fl_Input(10, 125, 305, 25, gettext("Path for font files"));	fpath->align(FL_ALIGN_TOP_LEFT);
+	locale = new Fl_Input(10, 175, 305, 25, gettext("Select locale"));	locale->align(FL_ALIGN_TOP_LEFT);
+
+	plast = new Fl_Check_Button(10, 210, 210, 25, gettext("Use plastic scheme"));
+	aexec = new Fl_Check_Button(10, 240, 210, 25, gettext("Execute after script loading"));
+	ifont = new Fl_Check_Button(10, 270, 210, 25, gettext("Use only internal font"));
+
+	Fl_Button *o;
+	o = new Fl_Button(240, 210, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o = new Fl_Return_Button(240, 240, 75, 25, gettext("OK"));	o->callback(prop_dlg_cb,wnd);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void settings_cb(Fl_Widget *, void *v)
+{
+	PropDlg *s = &prop_dlg;
+	s->graph = ((ScriptWindow *)v)->graph;
+	s->init();
+	s->wnd->set_modal();
+	s->wnd->show();
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/table.cpp b/mgllab/table.cpp
new file mode 100644
index 0000000..db88f37
--- /dev/null
+++ b/mgllab/table.cpp
@@ -0,0 +1,875 @@
+/* table.cpp is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <FL/Fl_Spinner.H>
+#include <FL/Fl_Output.H>
+#include <FL/Fl_Float_Input.H>
+#include <FL/Fl_Value_Input.H>
+#include <FL/Fl_Round_Button.H>
+#include "udav.h"
+//-----------------------------------------------------------------------------
+void addto_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *s = fl_input(gettext("Enter number for addition to data values"),0);
+	if(s)	{	mgl_data_add_num(e->var, atof(s));	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void subto_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *s = fl_input(gettext("Enter number for subtraction from data values"),0);
+	if(s)	{	mgl_data_sub_num(e->var, atof(s));	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void multo_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *s = fl_input(gettext("Enter number for multiplication of data values"),0);
+	if(s)	{	mgl_data_mul_num(e->var, atof(s));	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void divto_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *s = fl_input(gettext("Enter number for division of data values"),0);
+	if(s)	{	mgl_data_div_num(e->var, atof(s));	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+struct XYZDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Box *box;
+	Fl_Check_Button *ch;
+	Fl_Spinner *mx, *my, *mz;
+	XYZDlg()	{	memset(this,0,sizeof(XYZDlg));	create_dlg();	}
+	~XYZDlg()	{	delete wnd;	}
+	void create_dlg();
+} xyz_dlg;
+//-----------------------------------------------------------------------------
+void xyz_dlg_cb(Fl_Widget *, void *v)
+{	xyz_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void XYZDlg::create_dlg()
+{
+	wnd = new Fl_Double_Window(325, 125, gettext("Change data sizes"));
+	box = new Fl_Box(10, 10, 305, 40);
+	box->box(UDAV_THIN_UP_BOX);
+	box->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+
+	mx = new Fl_Spinner(30, 55, 75, 25, "mx");
+	mx->tooltip(gettext("New size of data on 1st dimension (x-direction)"));
+	my = new Fl_Spinner(135, 55, 75, 25, "my");
+	my->tooltip(gettext("New size of data on 2nd dimension (y-direction)"));
+	mz = new Fl_Spinner(240, 55, 75, 25, "mz");
+	mz->tooltip(gettext("New size of data on 3d dimension (z-direction)"));
+	ch = new Fl_Check_Button(15, 90, 95, 25);
+
+	Fl_Button *o;
+	o = new Fl_Button(125, 90, 85, 25, gettext("Cancel"));	o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Do nothing and close this window"));
+	o = new Fl_Return_Button(230, 90, 85, 25, gettext("Change"));o->callback(xyz_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Change (resize) data"));
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void new_dat_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	xyz_dlg.box->label(gettext("Specify new data size\nData will be zero filled"));
+	xyz_dlg.ch->label(gettext("not used"));	xyz_dlg.OK = false;
+	xyz_dlg.wnd->set_modal();		xyz_dlg.wnd->show();
+	while(xyz_dlg.wnd->shown())	Fl::wait();
+	if(xyz_dlg.OK)
+	{
+		e->var->Create(int(xyz_dlg.mx->value()),
+			int(xyz_dlg.my->value()), int(xyz_dlg.mz->value()));
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+void resize_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	xyz_dlg.box->label(gettext("Specify new data size\nData will be interpolated"));
+	xyz_dlg.ch->label(gettext("not used"));	xyz_dlg.OK = false;
+	xyz_dlg.wnd->set_modal();		xyz_dlg.wnd->show();
+	while(xyz_dlg.wnd->shown())	Fl::wait();
+	if(xyz_dlg.OK)
+	{
+		mglData d = e->var->Resize(int(xyz_dlg.mx->value()), int(xyz_dlg.my->value()), int(xyz_dlg.mz->value()));
+		mgl_data_set(e->var, &d);
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+void squeeze_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	xyz_dlg.box->label(gettext("Specify the skiping step\nEach m-th point will be skiped"));
+	xyz_dlg.ch->label(gettext("smoothed"));	xyz_dlg.OK = false;
+	xyz_dlg.wnd->set_modal();		xyz_dlg.wnd->show();
+	while(xyz_dlg.wnd->shown())	Fl::wait();
+	if(xyz_dlg.OK)
+	{
+		e->var->Squeeze(int(xyz_dlg.mx->value()), int(xyz_dlg.my->value()),
+					int(xyz_dlg.mz->value()), xyz_dlg.ch->value());
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+struct ChngDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Check_Button *dx, *dy, *dz;
+	Fl_Choice *kind, *type;
+	ChngDlg()	{	memset(this,0,sizeof(ChngDlg));	create_dlg();	}
+	~ChngDlg()	{	delete wnd;	}
+	void create_dlg();
+	void execute(mglData *d);
+} chng_dlg;
+//-----------------------------------------------------------------------------
+void chng_dlg_cb(Fl_Widget *, void *v)
+{	chng_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void ChngDlg::execute(mglData *d)
+{
+	char r[8]="3";
+	if(dx->value())	strcat(r,"x");
+	if(dy->value())	strcat(r,"y");
+	if(dz->value())	strcat(r,"z");
+	if(!r[0])	return;
+	if(type->value()==1)	r[0] = '5';
+	if(type->value()==2)	r[0] = ' ';
+	switch(kind->value())
+	{
+	case 0:	d->Smooth(r);		break;
+	case 1:	d->CumSum(r);		break;
+	case 2:	d->Integral(r);	break;
+	case 3:	d->Diff(r);		break;
+	case 4:	d->Diff2(r);		break;
+	case 5:	d->Swap(r);		break;
+	}
+}
+//-----------------------------------------------------------------------------
+void ChngDlg::create_dlg()
+{
+	Fl_Menu_Item k[]={{gettext("Smooth")}, {gettext("CumSum")}, { gettext("Integrate")},
+		{ gettext("Difference")}, { gettext("Double diff.")}, { gettext("Swap parts")}, {0}};
+	Fl_Menu_Item t[]={{gettext("Linear *3")}, {gettext("Linear *5")}, {gettext("Parabolic *5")},{0}};
+	wnd = new Fl_Double_Window(165, 215, gettext("Change data"));
+	kind = new Fl_Choice(10, 25, 145, 25, gettext("Type of operation"));
+	kind->align(FL_ALIGN_TOP_LEFT);	kind->copy(k);
+	dx = new Fl_Check_Button(10, 55, 140, 25, gettext("apply in x direction"));
+	dy = new Fl_Check_Button(10, 80, 140, 25, gettext("apply in y direction"));
+	dz = new Fl_Check_Button(10, 105, 140, 25, gettext("apply in z direction"));
+	type = new Fl_Choice(10, 145, 145, 25, gettext("Type of smoothing"));
+	type->align(FL_ALIGN_TOP_LEFT);	type->copy(t);
+
+	Fl_Button *o;
+	o = new Fl_Button(10, 180, 65, 25, gettext("Cancel"));	o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(90, 180, 65, 25, gettext("Do"));o->callback(chng_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void smooth_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	chng_dlg.kind->value(0);
+	chng_dlg.type->activate();	chng_dlg.OK = false;
+	chng_dlg.wnd->set_modal();	chng_dlg.wnd->show();
+	while(chng_dlg.wnd->shown())	Fl::wait();
+	if(chng_dlg.OK)
+	{	chng_dlg.execute(e->var);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void cumsum_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	chng_dlg.kind->value(1);
+	chng_dlg.type->deactivate();chng_dlg.OK = false;
+	chng_dlg.wnd->set_modal();	chng_dlg.wnd->show();
+	while(chng_dlg.wnd->shown())	Fl::wait();
+	if(chng_dlg.OK)
+	{	chng_dlg.execute(e->var);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void integr_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	chng_dlg.kind->value(2);
+	chng_dlg.type->deactivate();chng_dlg.OK = false;
+	chng_dlg.wnd->set_modal();	chng_dlg.wnd->show();
+	while(chng_dlg.wnd->shown())	Fl::wait();
+	if(chng_dlg.OK)
+	{	chng_dlg.execute(e->var);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void diff_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	chng_dlg.kind->value(3);
+	chng_dlg.type->deactivate();chng_dlg.OK = false;
+	chng_dlg.wnd->set_modal();	chng_dlg.wnd->show();
+	while(chng_dlg.wnd->shown())	Fl::wait();
+	if(chng_dlg.OK)
+	{	chng_dlg.execute(e->var);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void diff2_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	chng_dlg.kind->value(4);
+	chng_dlg.type->deactivate();chng_dlg.OK = false;
+	chng_dlg.wnd->set_modal();	chng_dlg.wnd->show();
+	while(chng_dlg.wnd->shown())	Fl::wait();
+	if(chng_dlg.OK)
+	{	chng_dlg.execute(e->var);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void swap_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	chng_dlg.kind->value(5);
+	chng_dlg.type->deactivate();chng_dlg.OK = false;
+	chng_dlg.wnd->set_modal();	chng_dlg.wnd->show();
+	while(chng_dlg.wnd->shown())	Fl::wait();
+	if(chng_dlg.OK)
+	{	chng_dlg.execute(e->var);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+struct NwdtDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Check_Button *dx, *dy, *dz;
+	Fl_Choice *kind;
+	Fl_Input *name;
+	NwdtDlg()	{	memset(this,0,sizeof(NwdtDlg));	create_dlg();	}
+	~NwdtDlg()	{	delete wnd;	}
+	void create_dlg();
+} nwdt_dlg;
+//-----------------------------------------------------------------------------
+void nwdt_dlg_cb(Fl_Widget *, void *v)
+{	nwdt_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void NwdtDlg::create_dlg()
+{
+	Fl_Menu_Item k[]={{gettext("Summation of")}, {gettext("Maximum of")}, { gettext("Minimum of")}, {0}};
+	wnd = new Fl_Double_Window(165, 215, gettext("Extract data"));
+	kind = new Fl_Choice(10, 25, 145, 25, gettext("Type of operation"));
+	kind->align(FL_ALIGN_TOP_LEFT);	kind->copy(k);
+	dx = new Fl_Check_Button(10, 55, 140, 25, gettext("apply in x direction"));
+	dy = new Fl_Check_Button(10, 80, 140, 25, gettext("apply in y direction"));
+	dz = new Fl_Check_Button(10, 105, 140, 25, gettext("apply in z direction"));
+	name = new Fl_Input(10, 145, 145, 25, gettext("Name for output"));
+	name->align(FL_ALIGN_TOP_LEFT);
+
+	Fl_Button *o;
+	o = new Fl_Button(10, 180, 65, 25, gettext("Cancel"));	o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o = new Fl_Return_Button(90, 180, 65, 25, gettext("Do"));o->callback(chng_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void asum_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	nwdt_dlg.kind->value(0);	nwdt_dlg.OK = false;
+	nwdt_dlg.wnd->set_modal();	nwdt_dlg.wnd->show();
+	while(nwdt_dlg.wnd->shown())	Fl::wait();
+	if(nwdt_dlg.OK)
+	{
+		char r[8]="";
+		if(nwdt_dlg.dx->value())	strcat(r,"x");
+		if(nwdt_dlg.dy->value())	strcat(r,"y");
+		if(nwdt_dlg.dz->value())	strcat(r,"z");
+		if(!r[0])	return;
+		if(!nwdt_dlg.name->value()[0] || !strcmp(nwdt_dlg.name->value(),e->label()))
+			fl_alert(gettext("Name for output variable should be differ from this name"));
+		else
+		{
+			mglData d = e->var->Sum(r);
+			mgl_data_set(Parse->AddVar(nwdt_dlg.name->value()), &d);
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+void amax_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	nwdt_dlg.kind->value(0);	nwdt_dlg.OK = false;
+	nwdt_dlg.wnd->set_modal();	nwdt_dlg.wnd->show();
+	while(nwdt_dlg.wnd->shown())	Fl::wait();
+	if(nwdt_dlg.OK)
+	{
+		char r[8]="";
+		if(nwdt_dlg.dx->value())	strcat(r,"x");
+		if(nwdt_dlg.dy->value())	strcat(r,"y");
+		if(nwdt_dlg.dz->value())	strcat(r,"z");
+		if(!r[0])	return;
+		if(!nwdt_dlg.name->value()[0] || !strcmp(nwdt_dlg.name->value(),e->label()))
+			fl_alert(gettext("Name for output variable should be differ from this name"));
+		else
+		{
+			mglData d = e->var->Max(r);
+			mgl_data_set(Parse->AddVar(nwdt_dlg.name->value()), &d);
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+void amin_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	nwdt_dlg.kind->value(0);	nwdt_dlg.OK = false;
+	nwdt_dlg.wnd->set_modal();	nwdt_dlg.wnd->show();
+	while(nwdt_dlg.wnd->shown())	Fl::wait();
+	if(nwdt_dlg.OK)
+	{
+		char r[8]="";
+		if(nwdt_dlg.dx->value())	strcat(r,"x");
+		if(nwdt_dlg.dy->value())	strcat(r,"y");
+		if(nwdt_dlg.dz->value())	strcat(r,"z");
+		if(!r[0])	return;
+		if(!nwdt_dlg.name->value()[0] || !strcmp(nwdt_dlg.name->value(), e->label()))
+			fl_alert(gettext("Name for output variable should be differ from this name"));
+		else
+		{
+			mglData d = e->var->Min(r);
+			mgl_data_set(Parse->AddVar(nwdt_dlg.name->value()), &d);
+		}
+	}
+}
+//-----------------------------------------------------------------------------
+void load_dat_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	char *newfile = fl_file_chooser(gettext("Load Data?"),
+		gettext("DAT Files (*.{dat,csv})\tAll Files (*)"), 0);
+	if(newfile != NULL)
+	{	e->var->Read(newfile);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void save_dat_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	char *newfile = fl_file_chooser(gettext("Save Data?"),
+		gettext("DAT Files (*.{dat,csv})\tAll Files (*)"), 0);
+	if(newfile != NULL)	e->var->Save(newfile);
+}
+//-----------------------------------------------------------------------------
+void exp_dat_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *scheme, *newfile = fl_file_chooser(gettext("Export Data?"),
+		gettext("PNG Files (*.png)\tAll Files (*)"), 0);
+	if(newfile != NULL)
+	{
+		scheme = fl_input(gettext("Enter color scheme"),MGL_DEF_SCH);
+		if(scheme)	e->var->Export(newfile,scheme);
+	}
+}
+//-----------------------------------------------------------------------------
+void imp_dat_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *scheme, *newfile = fl_file_chooser(gettext("Import Data?"),
+		gettext("PNG Files (*.png)\tAll Files (*)"), 0);
+	if (newfile != NULL)
+	{
+		scheme = fl_input(gettext("Enter color scheme"),MGL_DEF_SCH);
+		if(scheme)
+		{	e->var->Import(newfile,scheme);	e->refresh();	}
+	}
+}
+//-----------------------------------------------------------------------------
+void list_dat_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	mglData *d = e->var;
+	if(d->nx*d->ny+d->ny>1020)
+	{	fl_message(gettext("Too many numbers (>1000) on slice"));	return;	}
+	if(d->nz>1)	fl_message(gettext("Only current slice will be inserted"));
+	char *list = new char[16384];
+	strcpy(list,"list\t");
+	register long i,j;
+	char s[32];
+	for(j=0;j<d->ny;j++)
+	{
+		for(i=0;i<d->nx;i++)
+		{
+			snprintf(s,32,"%g\t",d->a[i+d->nx*(j+e->get_slice()*d->ny)]);
+			strcat(list,s);
+		}
+		if(j<d->ny-1)	strcat(list,"|\t");
+	}
+	textbuf->insert(0,list);
+	delete []list;
+}
+//-----------------------------------------------------------------------------
+void modify_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	const char *eq=fl_input(gettext("Enter formula for data modification\nHere x, y, z in range [0,1], u is data value"),0);
+	if (eq != NULL)	{	e->var->Modify(eq);	e->refresh();	}
+}
+//-----------------------------------------------------------------------------
+void plot_dat_cb(Fl_Widget *, void *v);
+//-----------------------------------------------------------------------------
+struct NrmDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Value_Input *min, *max;
+	Fl_Choice *dir;
+	Fl_Check_Button *sym;
+	NrmDlg()	{	memset(this,0,sizeof(NrmDlg));	create_dlg();	}
+	~NrmDlg()	{	delete wnd;	}
+	void create_dlg();
+} nrm_dlg;
+//-----------------------------------------------------------------------------
+void nrm_dlg_cb(Fl_Widget *, void *v)
+{	nrm_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void NrmDlg::create_dlg()
+{
+	Fl_Menu_Item k[]={{"x"}, {"y"}, { "z"}, {0}};
+	wnd = new Fl_Double_Window(135, 215);
+	min = new Fl_Value_Input(10, 25, 115, 25, gettext("Minimal value (v1)"));
+	min->align(FL_ALIGN_TOP_LEFT);
+	min->tooltip(gettext("Minimal value for resulting data values"));
+	max = new Fl_Value_Input(10, 70, 115, 25, gettext("Maximal value (v2)"));
+	max->align(FL_ALIGN_TOP_LEFT);
+	max->tooltip(gettext("Maximal value for resulting data values"));
+	dir = new Fl_Choice(10, 115, 115, 25, gettext("Direction"));
+	dir->align(FL_ALIGN_TOP_LEFT);	dir->copy(k);
+	dir->tooltip(gettext("Direction along which data will be filled"));
+	sym = new Fl_Check_Button(10, 115, 115, 25, gettext("Symetrical range"));
+	sym->tooltip(gettext("Normalize in symmetrical range: -max(|v1|,|v2|) ... max(|v1|,|v2|)"));
+
+	Fl_Button *o;
+	o = new Fl_Button(25, 150, 85, 25, gettext("Cancel"));	o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Do nothing and close this window"));
+	o = new Fl_Return_Button(25, 180, 85, 25, gettext("Change"));o->callback(nrm_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Change data values and close this window"));
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void fill_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	nrm_dlg.OK = false;			nrm_dlg.wnd->label(gettext("Fill in range"));
+	nrm_dlg.dir->show();		nrm_dlg.sym->hide();
+	nrm_dlg.wnd->set_modal();	nrm_dlg.wnd->show();
+	while(nrm_dlg.wnd->shown())	Fl::wait();
+	if(nrm_dlg.OK)
+	{
+		char r='x';
+		if(nrm_dlg.dir->value()==1)	r='y';
+		if(nrm_dlg.dir->value()==2)	r='z';
+		e->var->Fill(nrm_dlg.min->value(),nrm_dlg.max->value(),r);
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+void normal_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	nrm_dlg.OK = false;			nrm_dlg.wnd->label(gettext("Normalize data"));
+	nrm_dlg.dir->hide();		nrm_dlg.sym->show();
+	nrm_dlg.wnd->set_modal();	nrm_dlg.wnd->show();
+	while(nrm_dlg.wnd->shown())	Fl::wait();
+	if(nrm_dlg.OK)
+	{
+		e->var->Norm(nrm_dlg.min->value(), nrm_dlg.max->value(), nrm_dlg.sym->value());
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+struct CropDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Input *x1,*x2, *y1,*y2, *z1,*z2;
+	CropDlg()	{	memset(this,0,sizeof(CropDlg));	create_dlg();	}
+	~CropDlg()	{	delete wnd;	}
+	void create_dlg();
+} crop_dlg;
+//-----------------------------------------------------------------------------
+void crop_dlg_cb(Fl_Widget *, void *v)
+{	crop_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void CropDlg::create_dlg()
+{
+	wnd = new Fl_Double_Window(230, 155, gettext("Crop data"));
+	x1 = new Fl_Input(45, 25, 80, 25, gettext("Lower bound"));	x1->align(FL_ALIGN_TOP);
+	x2 = new Fl_Input(140, 25, 80, 25, gettext("Upper bound"));	x2->align(FL_ALIGN_TOP);
+	y1 = new Fl_Input(45, 55, 80, 25);
+	y2 = new Fl_Input(140, 55, 80, 25);
+	z1 = new Fl_Input(45, 85, 80, 25);
+	z2 = new Fl_Input(140, 85, 80, 25);
+
+	new Fl_Box(15, 25, 25, 25, "X");
+	new Fl_Box(15, 55, 25, 25, "Y");
+	new Fl_Box(15, 85, 25, 25, "Z");
+	Fl_Button *o;
+	o = new Fl_Button(45, 120, 75, 25, gettext("Cancel"));		o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Do nothing and close this window"));
+	o = new Fl_Return_Button(145, 120, 75, 25, gettext("Crop"));	o->callback(crop_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Change data values and close this window"));
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void crop_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	crop_dlg.OK = false;
+	crop_dlg.wnd->set_modal();	crop_dlg.wnd->show();
+	while(crop_dlg.wnd->shown())	Fl::wait();
+	if(crop_dlg.OK)
+	{
+		int n1,n2;
+		n1 = 0;	n2 = e->var->nx;
+		if(crop_dlg.x1->value()[0])	n1 = atoi(crop_dlg.x1->value());
+		if(crop_dlg.x2->value()[0])	n2 = atoi(crop_dlg.x2->value());
+		e->var->Crop(n1, n2, 'x');
+		n1 = 0;	n2 = e->var->ny;
+		if(crop_dlg.y1->value()[0])	n1 = atoi(crop_dlg.y1->value());
+		if(crop_dlg.y2->value()[0])	n2 = atoi(crop_dlg.y2->value());
+		e->var->Crop(n1, n2, 'y');
+		n1 = 0;	n2 = e->var->nz;
+		if(crop_dlg.z1->value()[0])	n1 = atoi(crop_dlg.z1->value());
+		if(crop_dlg.z2->value()[0])	n2 = atoi(crop_dlg.z2->value());
+		e->var->Crop(n1, n2, 'z');
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+struct TrspDlg
+{
+	Fl_Window *wnd;
+	bool OK;
+
+	Fl_Round_Button *xyz, *xzy, *yxz, *yzx, *zxy, *zyx;
+	TrspDlg()	{	memset(this,0,sizeof(TrspDlg));	create_dlg();	}
+	~TrspDlg()	{	delete wnd;	}
+	void create_dlg();
+} trsp_dlg;
+//-----------------------------------------------------------------------------
+void trsp_dlg_cb(Fl_Widget *, void *v)
+{	trsp_dlg.OK = true;	((Fl_Window *)v)->hide();	}
+//-----------------------------------------------------------------------------
+void trsp_rad_cb(Fl_Widget *w, void *v)
+{
+	TrspDlg* e = (TrspDlg*)v;
+	e->xyz->value(0);	e->xzy->value(0);
+	e->yxz->value(0);	e->yzx->value(0);
+	e->zxy->value(0);	e->zyx->value(0);
+	((Fl_Round_Button *)w)->setonly();
+}
+//-----------------------------------------------------------------------------
+void TrspDlg::create_dlg()
+{
+	wnd = new Fl_Double_Window(220, 170, gettext("Transpose data"));
+	Fl_Group *g = new Fl_Group(10, 30, 200, 90, gettext("Select new order of dimensions"));
+	g->box(FL_DOWN_BOX);
+	yxz = new Fl_Round_Button(20, 40, 75, 25, "y - x - z");	yxz->callback(trsp_rad_cb,this);
+	zyx = new Fl_Round_Button(20, 65, 75, 25, "z - y - x");	zyx->callback(trsp_rad_cb,this);
+	zxy = new Fl_Round_Button(20, 90, 75, 25, "z - x - y");	zxy->callback(trsp_rad_cb,this);
+	yzx = new Fl_Round_Button(100, 40, 75, 25, "y - z - x");yzx->callback(trsp_rad_cb,this);
+	xzy = new Fl_Round_Button(100, 65, 75, 25, "x - z - y");xzy->callback(trsp_rad_cb,this);
+	xyz = new Fl_Round_Button(100, 90, 75, 25, "x - y - z");xyz->callback(trsp_rad_cb,this);
+	g->end();
+
+	Fl_Button *o;
+	o = new Fl_Button(25, 130, 75, 25, gettext("Cancel"));	o->callback(close_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Do nothing and close this window"));
+	o = new Fl_Return_Button(125, 130, 75, 25, gettext("Do"));	o->callback(trsp_dlg_cb,wnd);
+	o->box(UDAV_UP_BOX);	o->down_box(UDAV_DOWN_BOX);
+	o->tooltip(gettext("Change data values and close this window"));
+	wnd->end();
+}
+//-----------------------------------------------------------------------------
+void transp_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	trsp_dlg.OK = false;
+	trsp_dlg.wnd->set_modal();	trsp_dlg.wnd->show();
+	while(trsp_dlg.wnd->shown())	Fl::wait();
+	if(trsp_dlg.OK)
+	{
+		if(trsp_dlg.xyz->value())	e->var->Transpose("xyz");
+		if(trsp_dlg.xzy->value())	e->var->Transpose("xzy");
+		if(trsp_dlg.yxz->value())	e->var->Transpose("yxz");
+		if(trsp_dlg.yzx->value())	e->var->Transpose("yzx");
+		if(trsp_dlg.zxy->value())	e->var->Transpose("zxy");
+		if(trsp_dlg.zyx->value())	e->var->Transpose("zyx");
+		e->refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+void first_sl_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	e->slice->value(0);
+	e->set_slice(0);
+	e->go_home();
+}
+//-----------------------------------------------------------------------------
+void last_sl_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	e->slice->value(e->num_slice()-1);
+	e->set_slice(e->num_slice()-1);
+	e->go_home();
+}
+//-----------------------------------------------------------------------------
+void prev_sl_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	int p = int(e->slice->value())-1;
+	if(p<0)	p = 0;
+	e->slice->value(p);		e->set_slice(p);
+	e->go_home();
+}
+//-----------------------------------------------------------------------------
+void next_sl_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	int p = int(e->slice->value())+1;
+	if(p>=e->num_slice())	p = e->num_slice()-1;
+	e->slice->value(p);		e->set_slice(p);
+	e->go_home();
+}
+//-----------------------------------------------------------------------------
+void first_cl_cb(Fl_Widget*, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	e->go_home();
+}
+//-----------------------------------------------------------------------------
+void change_sl_cb(Fl_Widget*w, void*v)
+{
+	TableWindow* e = (TableWindow*)v;
+	e->set_slice(long(e->slice->value()));
+	e->go_home();
+}
+//-----------------------------------------------------------------------------
+Fl_Menu_Item tablemenu[60] = {
+	{ gettext("General"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("Load from file"),	0, load_dat_cb },
+		{ gettext("Import from PNG"),0, imp_dat_cb },
+		{ gettext("Save to file"),	0, save_dat_cb },
+		{ gettext("Export to PNG"),	0, exp_dat_cb, 0, FL_MENU_DIVIDER },
+		{ gettext("Insert as list"),	0, list_dat_cb },
+		{ gettext("Plot data"),		0, plot_dat_cb },
+//		{ gettext("Info for data"),	0, info_dat_cb },
+		{ 0 },
+	{ gettext("Sizes"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("Create new"),	0, new_dat_cb },
+		{ gettext("Resize"),		0, resize_cb },
+		{ gettext("Squeeze"),	0, squeeze_cb },
+		{ gettext("Crop"),		0, crop_cb },
+		{ gettext("Transpose"),	0, transp_cb },
+//		{ gettext("Extend"),		0, extend_cb },
+		{ 0 },
+	{ gettext("Fill"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("By formula"),	0, modify_cb },
+		{ gettext("In range"),	0, fill_cb },
+		{ gettext("Normalize"),	0, normal_cb },
+		{ 0 },
+	{ gettext("Change"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("Smooth"),		0, smooth_cb },
+		{ gettext("CumSum"),		0, cumsum_cb },
+		{ gettext("Integrate"),	0, integr_cb },
+		{ gettext("Difference"),	0, diff_cb },
+		{ gettext("Double diff."),	0, diff2_cb },
+		{ gettext("Swap parts"),	0, swap_cb },
+		{ 0 },
+	{ gettext("Another"), 0, 0, 0, FL_SUBMENU },
+//		{ gettext("Histogram of"),	0, hist_cb },
+		{ gettext("Summation of"),	0, asum_cb },
+		{ gettext("Maximum of"),	0, amax_cb },
+		{ gettext("Minimum of"),	0, amin_cb },
+		{ 0 },
+	{ gettext("Operations"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("Add to"),		0, addto_cb },
+		{ gettext("Subtract to"),0, subto_cb },
+		{ gettext("Multiply by"),0, multo_cb },
+		{ gettext("Divide by"),	0, divto_cb },
+		{ 0 },
+	{ gettext("Navigation"), 0, 0, 0, FL_SUBMENU },
+		{ gettext("First slice"), FL_CTRL + FL_F + 1, first_sl_cb },
+		{ gettext("Prev slice"), FL_CTRL + FL_F + 2, prev_sl_cb },
+		{ gettext("Next slice"), FL_CTRL + FL_F + 3, next_sl_cb },
+		{ gettext("Last slice"),	FL_CTRL + FL_F + 4, last_sl_cb, 0, FL_MENU_DIVIDER },
+		{ gettext("First cell"), FL_ALT + FL_F + 1, first_cl_cb },
+//		{ gettext("Last cell"), FL_ALT + FL_F + 2, last_cl_cb },
+//		{ gettext("Center grid"), FL_ALT + FL_F + 3, center_cl_cb },
+		{ 0 },
+	{ 0 }
+};
+//-----------------------------------------------------------------------------
+#include "xpm/document-new.xpm"
+#include "xpm/plot.xpm"
+#include "xpm/document-open.xpm"
+#include "xpm/document-save.xpm"
+#include "xpm/document-import.xpm"
+#include "xpm/document-export.xpm"
+#include "xpm/format-indent-more.xpm"
+#include "xpm/diff.xpm"
+#include "xpm/func.xpm"
+#include "xpm/size.xpm"
+#include "xpm/tran.xpm"
+#include "xpm/crop.xpm"
+#include "xpm/go-first.xpm"
+#include "xpm/go-last.xpm"
+TableWindow::TableWindow(int x, int y, int w, int h, const char* t) : Fl_Window(x, y, w, h, t)
+{
+	var = 0;
+//	menu = new Fl_Menu_Bar(0, 0, w, 30);
+//	menu->copy(tablemenu, this);
+	Fl_Button *o;
+	Fl_Group *g;
+
+
+	g = new Fl_Group(0,0,30,350);
+	o = new Fl_Button(0, 0, 25, 25);	o->image(new Fl_Pixmap(document_new_xpm));
+	o->callback(new_dat_cb,this);		o->tooltip(gettext("Create new data with zero filling"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 25, 25, 25);	o->image(new Fl_Pixmap(document_open_xpm));
+	o->callback(load_dat_cb,this);		o->tooltip(gettext("Load data from file"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 50, 25, 25);	o->image(new Fl_Pixmap(document_import_xpm));
+	o->callback(imp_dat_cb,this);		o->tooltip(gettext("Import data from PNG file"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 75, 25, 25);	o->image(new Fl_Pixmap(document_save_xpm));
+	o->callback(save_dat_cb,this);		o->tooltip(gettext("Save data to file"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 100, 25, 25);	o->image(new Fl_Pixmap(document_export_xpm));
+	o->callback(exp_dat_cb,this);		o->tooltip(gettext("Export data to PNG file"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+
+	o = new Fl_Button(0, 130, 25, 25);	o->image(new Fl_Pixmap(format_indent_more_xpm));
+	o->callback(list_dat_cb,this);		o->tooltip(gettext("Insert to script as 'list' command"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 155, 25, 25);	o->image(new Fl_Pixmap(plot_xpm));
+	o->callback(plot_dat_cb,this);		o->tooltip(gettext("Plot data"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+
+	o = new Fl_Button(0, 185, 25, 25);	o->image(new Fl_Pixmap(diff_xpm));
+	o->callback(smooth_cb,this);		o->tooltip(gettext("Apply operator (smoothing, integration, difference ...) to data"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 210, 25, 25);	o->image(new Fl_Pixmap(func_xpm));
+	o->callback(modify_cb,this);		o->tooltip(gettext("Fill data by formula"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 235, 25, 25);	o->image(new Fl_Pixmap(size_xpm));
+	o->callback(resize_cb,this);		o->tooltip(gettext("Resize data with smoothing"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 260, 25, 25);	o->image(new Fl_Pixmap(crop_xpm));
+	o->callback(crop_cb,this);		o->tooltip(gettext("Crop (cut off edges) data"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(0, 285, 25, 25);	o->image(new Fl_Pixmap(tran_xpm));
+	o->callback(transp_cb,this);		o->tooltip(gettext("Transpose data dimensions"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	g->end();	g->resizable(0);
+
+
+	g = new Fl_Group(30,0,200,30);
+	o = new Fl_Button(30, 0, 25, 25);	o->image(new Fl_Pixmap(go_first_xpm));
+	o->callback(first_sl_cb,this);		o->tooltip(gettext("Go to first slice (Ctrl-F1)"));
+//	o->box(FL_PLASTIC_UP_BOX);	o->down_box(FL_PLASTIC_DOWN_BOX);
+	slice = new Fl_Counter(55, 0, 90, 25, 0);	slice->callback(change_sl_cb,this);
+	slice->lstep(10);	slice->step(1);	slice->tooltip(gettext("Id of slice on third (z-) dimension"));
+//	slice->box(FL_PLASTIC_UP_BOX);//	slice->down_box(FL_PLASTIC_DOWN_BOX);
+	o = new Fl_Button(147, 0, 25, 25);	o->image(new Fl_Pixmap(go_last_xpm));
+	o->callback(last_sl_cb,this);		o->tooltip(gettext("Go to last slice (Ctrl-F4)"));
+	g->end();	g->resizable(0);
+
+	data = new Fl_Data_Table(30,30,w-30,h-30);
+	data->row_header(1);	data->row_header_width(70);
+	data->row_resize(1);	data->rows(1);
+	data->row_height_all(25);
+	data->col_header(1);	data->col_header_height(25);
+	data->col_resize(1);	data->cols(1);
+	data->col_width_all(70);
+
+	end();	resizable(data);
+//	callback(close_table_cb, this);
+}
+//-----------------------------------------------------------------------------
+TableWindow::~TableWindow()
+{	parent()->deactivate();
+	(parent()->parent())->remove(parent());
+	parent()->remove(this);	delete parent();	}
+//-----------------------------------------------------------------------------
+void delete_cb(void *v)	{	if(v)	delete (TableWindow *)v;	}
+//-----------------------------------------------------------------------------
+void TableWindow::update(mglVar *v)
+{
+	if(v==0)	return;
+	char ss[1024];
+	wcstombs(ss,v->s.c_str(),1024);
+	label(ss);	v->func = delete_cb;
+//	if(var)	var->o = 0;
+	var = v; 	v->o = this;
+	refresh();
+}
+//-----------------------------------------------------------------------------
+void TableWindow::refresh()
+{
+	if(var==0)	return;
+	deactivate();	nz = var->nz;
+	sl = 0;	slice->range(0,nz-1);
+
+	data->rows(var->ny);	data->cols(var->nx);
+	data->ny = var->ny;	data->nx = var->nx;
+	data->data = var->a;
+	activate();
+//	show();
+}
+//-----------------------------------------------------------------------------
+void TableWindow::set_slice(long s)
+{
+	if(s>=0 && s<nz)
+	{
+		sl = s;
+		data->data = var->a + var->nx * var->ny * sl;
+		refresh();
+	}
+}
+//-----------------------------------------------------------------------------
+void TableWindow::go_home()
+{
+}
+//-----------------------------------------------------------------------------
diff --git a/mgllab/udav.h b/mgllab/udav.h
new file mode 100644
index 0000000..0fa1b35
--- /dev/null
+++ b/mgllab/udav.h
@@ -0,0 +1,291 @@
+/* udav.h is part of UDAV
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+//-----------------------------------------------------------------------------
+#ifndef _UDAV_H_
+#define _UDAV_H_
+//-----------------------------------------------------------------------------
+#ifdef __MWERKS__
+# define FL_DLL
+#endif
+#ifdef USE_GETTEXT
+	#include <libintl.h>
+#else
+	#define gettext(x)	(x)
+#endif
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/fl_ask.H>
+#include <FL/Fl_File_Chooser.H>
+#include <FL/Fl_Menu_Bar.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Return_Button.H>
+#include <FL/Fl_Text_Buffer.H>
+#include <FL/Fl_Text_Editor.H>
+#include <FL/Fl_Pixmap.H>
+#include <FL/Fl_Counter.H>
+#include <Fl/Fl_Scroll.H>
+#include <FL/Fl_Tabs.H>
+#include <FL/Fl_Help_View.H>
+#include <Fl/Fl_Table.H>
+#include <Fl/Fl_Round_Button.H>
+#include <Fl/Fl_Float_Input.H>
+#include <Fl/Fl_Multiline_Input.H>
+//-----------------------------------------------------------------------------
+#ifdef USE_PLASTIC
+	#define UDAV_UP_BOX			FL_PLASTIC_UP_BOX
+	#define UDAV_DOWN_BOX		FL_PLASTIC_DOWN_BOX
+	#define UDAV_EDIT_BOX		FL_PLASTIC_THIN_DOWN_BOX
+	#define UDAV_THIN_UP_BOX	FL_PLASTIC_THIN_UP_BOX
+#else
+	#define UDAV_UP_BOX			FL_GTK_UP_BOX
+	#define UDAV_DOWN_BOX		FL_GTK_DOWN_BOX
+	#define UDAV_EDIT_BOX		FL_GTK_DOWN_BOX
+	#define UDAV_THIN_UP_BOX	FL_GTK_THIN_UP_BOX
+#endif
+//-----------------------------------------------------------------------------
+#include "mgl2/fltk.h"
+//-----------------------------------------------------------------------------
+extern mglParse *Parse;
+extern Fl_Menu_Item colors[];
+extern Fl_Preferences pref;
+extern char *docdir;
+class Fl_MGL;
+//-----------------------------------------------------------------------------
+class Fl_Data_Table : public Fl_Table
+{
+private:
+	int row, col;
+	Fl_Input* input;
+protected:
+	void draw_cell(TableContext context, int R, int C, int X, int Y, int W, int H);
+	static void event_callback(Fl_Widget*, void*v)
+	{	((Fl_Data_Table*)v)->cell_click();	}
+	void cell_click();
+
+public:
+	mreal *data;
+	int nx, ny;
+
+	Fl_Data_Table(int x, int y, int w, int h, const char *l=0);
+    ~Fl_Data_Table() { }
+
+	void set_value();
+    void rows(int val) { if (input->visible()) input->do_callback(); Fl_Table::rows(val); }
+    void cols(int val) { if (input->visible()) input->do_callback(); Fl_Table::cols(val); }
+    inline int rows() { return Fl_Table::rows(); }
+    inline int cols() { return Fl_Table::cols(); }
+};
+//-----------------------------------------------------------------------------
+struct AnimateDlg
+{
+	friend void animate_dlg_cb(Fl_Widget *, void *v);
+	friend void animate_rad_cb(Fl_Widget *, void *v);
+	friend void fill_animate(const char *text);
+	friend void animate_put_cb(Fl_Widget *, void *);
+public:
+	Fl_Window* wnd;
+	int OK;
+	AnimateDlg()	{	memset(this,0,sizeof(AnimateDlg));	create_dlg();	}
+	~AnimateDlg()	{	delete wnd;	}
+	void FillResult(Fl_MGL* e);
+protected:
+	bool swap;
+	Fl_Round_Button *rt, *rv;
+	Fl_Multiline_Input *txt;
+	Fl_Float_Input *x0, *x1, *dx, *dt;
+	Fl_Check_Button *save;
+	void create_dlg();
+};
+//-----------------------------------------------------------------------------
+class Fl_MGL : public Fl_MGLView, public mglDraw
+{
+friend class AnimateDlg;
+public:
+	Fl_Widget *status;		///< StatusBar for mouse coordinates
+	const char *AnimBuf;		///< buffer for animation
+	const char **AnimS0;
+	int AnimNum;
+	mreal AnimDelay;
+
+	Fl_MGL(int x, int y, int w, int h, const char *label=0);
+	~Fl_MGL();
+
+	/// Drawing itself
+	int Draw(mglGraph *);
+	/// Update (redraw) plot
+	void update();
+	/// Set main scr and optional pre scripts for execution
+	void scripts(char *scr, char *pre);
+	/// Clear scripts internally saved
+	void clear_scripts();
+	/// Show next frame
+	void next_frame();
+	/// Show prev frame
+	void prev_frame();
+
+protected:
+	char *Args[1000], *ArgBuf;
+	int NArgs, ArgCur;
+
+	char *script;		///< main script
+	char *script_pre;	///< script with settings
+};
+//-----------------------------------------------------------------------------
+struct TableWindow : public Fl_Window
+{
+public:
+	TableWindow(int x, int y, int w, int h, const char* t=0);
+	~TableWindow();
+	void update(mglVar *v);
+	void refresh();
+	void set_slice(long s);
+	inline long get_slice() { return sl; }
+	inline long num_slice()	{	return nz;	}
+	void go_home();
+
+	Fl_Data_Table *data;
+	Fl_Menu_Bar	*menu;
+//	Fl_Output *main;
+	Fl_Counter *slice;
+	mglData *var;
+protected:
+//	long nx,ny,nz;
+	long nz;
+	long sl;		// current slice
+	char sl_id[64];	// slice id
+};
+//-----------------------------------------------------------------------------
+class SetupDlg
+{
+public:
+	Fl_Window *wnd;
+	bool OK;
+	Fl_Input *templ;
+
+	SetupDlg()	{	memset(this,0,sizeof(SetupDlg));	}
+	~SetupDlg()	{	delete wnd;	}
+	void CreateDlg();
+	char *ToScript();
+private:
+	Fl_Input *xmin, *ymin, *zmin, *cmin;
+	Fl_Input *xmax, *ymax, *zmax, *cmax;
+	Fl_Input *xorg, *yorg, *zorg;
+	Fl_Input *xlab, *ylab, *zlab, *font;
+	Fl_Choice *xpos, *ypos, *zpos, *axial, *cid[10];
+	Fl_Input *xtik, *ytik, *ztik;
+	Fl_Input *xsub, *ysub, *zsub;
+	Fl_Input *alphad, *ambient, *basew, *mesh, *size;
+	Fl_Check_Button *alpha, *light, *rotate, *lid[10];
+	Fl_Input *xid[10], *yid[10], *zid[10], *bid[10];
+	Fl_Help_View *code;
+
+	void CreateGen();
+	void CreateLid();
+};
+//-----------------------------------------------------------------------------
+class ScriptWindow : public Fl_Double_Window
+{
+public:
+	ScriptWindow(int w, int h, const char* t);
+	~ScriptWindow();
+
+	Fl_Window	*replace_dlg;
+	Fl_Input	*replace_find;
+	Fl_Input	*replace_with;
+	Fl_Button	*replace_all;
+	Fl_Return_Button	*replace_next;
+	Fl_Button	*replace_cancel;
+	Fl_Text_Editor		*editor;
+	Fl_Menu_Bar	*menu;
+	Fl_Tabs *ltab, *rtab;
+	Fl_Help_View *hd;
+	Fl_Input *link_cmd;
+	Fl_Group *ghelp;
+	Fl_Browser *var;
+	Fl_Box *status;
+
+	void mem_init();
+	void mem_pressed(int n);
+	SetupDlg 	*setup_dlg;
+	char		search[256];
+	Fl_MGL		*graph;
+};
+//-----------------------------------------------------------------------------
+// Editor window functions
+void find2_cb(Fl_Widget *, void *);
+void replall_cb(Fl_Widget *, void *);
+void replace2_cb(Fl_Widget *, void *);
+void replcan_cb(Fl_Widget *, void *);
+void insert_cb(Fl_Widget *, void *);
+void paste_cb(Fl_Widget *, void *);
+void replace_cb(Fl_Widget *, void *);
+void copy_cb(Fl_Widget *, void *);
+void cut_cb(Fl_Widget *, void *);
+void find_cb(Fl_Widget *, void *);
+void delete_cb(Fl_Widget *, void *);
+void changed_cb(int, int nInserted, int nDeleted,int, const char*, void* v);
+//-----------------------------------------------------------------------------
+// General callback functions
+void new_cb(Fl_Widget *, void *);
+void open_cb(Fl_Widget *, void *);
+void save_cb(Fl_Widget*, void*);
+void saveas_cb(Fl_Widget*, void*);
+void help_cb(Fl_Widget*, void*);
+//-----------------------------------------------------------------------------
+// Graphical callback functions
+void setup_cb(Fl_Widget *, void *);
+void style_cb(Fl_Widget *, void *);
+void option_cb(Fl_Widget *, void *);
+void argument_cb(Fl_Widget *, void *);
+void variables_cb(Fl_Widget *, void *);
+void settings_cb(Fl_Widget *, void *);
+void command_cb(Fl_Widget *, void *);
+//-----------------------------------------------------------------------------
+// Dialogs callback functions
+void close_dlg_cb(Fl_Widget *w, void *);
+void font_cb(Fl_Widget *, void *v);
+void line_cb(Fl_Widget *, void *v);
+void face_cb(Fl_Widget *, void *v);
+void data_cb(Fl_Widget *, void *v);
+//-----------------------------------------------------------------------------
+void style_init(void);
+int check_save(void);
+void load_file(char *newfile, int ipos);
+void save_file(char *newfile);
+Fl_Widget *add_editor(ScriptWindow *w);
+Fl_Widget *add_mem(ScriptWindow *w);
+void set_title(Fl_Window* w);
+//-----------------------------------------------------------------------------
+// Animation
+void animate_cb(Fl_Widget *, void *v);
+void fill_animate(const char *text);
+//-----------------------------------------------------------------------------
+Fl_Widget *add_help(ScriptWindow *w);
+void help_cb(Fl_Widget*, void*v);
+void example_cb(Fl_Widget*, void*v);
+void about_cb(Fl_Widget*, void*);
+//-----------------------------------------------------------------------------
+void newcmd_cb(Fl_Widget*,void*);
+//-----------------------------------------------------------------------------
+extern Fl_Text_Buffer	*textbuf;
+extern char	filename[256];
+extern int	changed;
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
diff --git a/mgllab/udav.rc b/mgllab/udav.rc
new file mode 100644
index 0000000..e557119
--- /dev/null
+++ b/mgllab/udav.rc
@@ -0,0 +1,24 @@
+/* udav.rc is part of UDAV
+ * Copyright (C) 2007 Alexey Balakin <balakin at appl.sci-nnov.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+//-----------------------------------------------------------------------------
+#include <windows.h>
+
+#define APPICON 1001
+#define APPICON48 1002
+
+APPICON ICON "udav.ico"
+APPICON48 ICON "udav48.ico"
diff --git a/mgllab/udav48.ico b/mgllab/udav48.ico
new file mode 100644
index 0000000..a286d6d
Binary files /dev/null and b/mgllab/udav48.ico differ
diff --git a/mgllab/write.cpp b/mgllab/write.cpp
new file mode 100644
index 0000000..51b2775
--- /dev/null
+++ b/mgllab/write.cpp
@@ -0,0 +1,17 @@
+/* write.cpp is part of Math Graphic Library
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+//-----------------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 531202b..be81d91 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(mgl_src
 	addon.cpp axis.cpp base_cf.cpp base.cpp canvas_cf.cpp canvas.cpp cont.cpp crust.cpp
-	complex.cpp complex_io.cpp
+	complex.cpp complex_io.cpp fft.cpp
 	data.cpp data_io.cpp data_new.cpp data_png.cpp def_font.cpp
 	export_2d.cpp export_3d.cpp eval.cpp evalp.cpp exec.cpp export.cpp
 	fit.cpp font.cpp obj.cpp other.cpp parser.cpp pde.cpp pixel.cpp
@@ -42,11 +42,17 @@ add_compiler_export_flags()
 add_library(mgl SHARED ${mgl_src} ${mgl_hdr})
 add_library(mgl-static STATIC ${mgl_src} ${mgl_hdr})
 set_target_properties(mgl PROPERTIES CLEAN_DIRECT_OUTPUT 1)
-set_target_properties(mgl-static PROPERTIES OUTPUT_NAME "mgl")
 set_target_properties(mgl-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 set_target_properties(mgl-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
 generate_export_header(mgl EXPORT_FILE_NAME ../include/mgl2/dllexport.h)
 
+if(enable-mgl2)
+	set_target_properties(mgl PROPERTIES OUTPUT_NAME "mgl2")
+	set_target_properties(mgl-static PROPERTIES OUTPUT_NAME "mgl2")
+else(enable-mgl2)
+	set_target_properties(mgl-static PROPERTIES OUTPUT_NAME "mgl")
+endif(enable-mgl2)
+
 if(MGL_HAVE_LTDL)
 	target_link_libraries(mgl ${LTDL_LIB})
 	include_directories(${LTDL_INCLUDE_DIR})
@@ -102,9 +108,32 @@ if(MGL_HAVE_ZLIB)
 endif(MGL_HAVE_ZLIB)
 
 if(MGL_HAVE_MPI)
-	target_link_libraries(mgl ${MPI_LIBRARIES} )
+	add_library(mgl-mpi SHARED mpi.cpp ../include/mgl2/mpi.h)
+	add_library(mgl-mpi-static STATIC mpi.cpp ../include/mgl2/mpi.h)
+	set_target_properties(mgl-mpi PROPERTIES CLEAN_DIRECT_OUTPUT 1)
+	set_target_properties(mgl-mpi PROPERTIES DEFINE_SYMBOL "mgl_EXPORTS")
+	set_target_properties(mgl-mpi-static PROPERTIES OUTPUT_NAME "mgl-mpi")
+	set_target_properties(mgl-mpi-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
+	set_target_properties(mgl-mpi-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
+
+	if(enable-mgl2)
+		set_target_properties(mgl-mpi PROPERTIES OUTPUT_NAME "mgl2-mpi")
+		set_target_properties(mgl-mpi-static PROPERTIES OUTPUT_NAME "mgl2-mpi")
+	else(enable-mgl2)
+		set_target_properties(mgl-mpi-static PROPERTIES OUTPUT_NAME "mgl-mpi")
+	endif(enable-mgl2)
+
+	target_link_libraries(mgl-mpi ${MPI_LIBRARIES} )
 #	include_directories(${MPI_C_INCLUDE_PATH})
 	include_directories(${MPI_CXX_INCLUDE_PATH})
+
+	set_target_properties(mgl-mpi PROPERTIES SOVERSION 7.0.0)
+	install(
+		TARGETS mgl-mpi mgl-mpi-static
+		RUNTIME DESTINATION bin
+		ARCHIVE DESTINATION ${MGL_LIB_INSTALL_DIR}
+		LIBRARY DESTINATION ${MGL_LIB_INSTALL_DIR}
+	)
 endif(MGL_HAVE_MPI)
 
 if(M_LIB)
diff --git a/src/addon.cpp b/src/addon.cpp
index 4969058..1cd87bf 100644
--- a/src/addon.cpp
+++ b/src/addon.cpp
@@ -28,6 +28,9 @@
 #include "mgl2/addon.h"
 #include "mgl2/data.h"
 //-----------------------------------------------------------------------------
+dual MGL_EXPORT mgl_expi(dual a)	{	return exp(dual(0,1)*a);	}
+dual MGL_EXPORT mgl_expi(double a)	{	return dual(cos(a),sin(a));	}
+//-----------------------------------------------------------------------------
 void MGL_EXPORT mgl_strcls(char *str)
 {
 	size_t len = strlen(str),i,n;
@@ -82,7 +85,7 @@ void MGL_EXPORT mgl_test(const char *str, ...)
 	char buf[256];
 	va_list lst;
 	va_start(lst,str);
-	vsprintf(buf,str,lst);
+	vsnprintf(buf,256,str,lst);
 	va_end(lst);
 	printf("TEST: %s\n",buf);
 	fflush(stdout);
@@ -93,7 +96,7 @@ void MGL_EXPORT mgl_info(const char *str, ...)
 	char buf[256];
 	va_list lst;
 	va_start(lst,str);
-	vsprintf(buf,str,lst);
+	vsnprintf(buf,256,str,lst);
 	va_end(lst);
 	printf("%s",buf);
 	FILE *fp = fopen("info.txt","at");
diff --git a/src/axis.cpp b/src/axis.cpp
index c0dcdb4..acbbd29 100644
--- a/src/axis.cpp
+++ b/src/axis.cpp
@@ -25,7 +25,7 @@
 //-----------------------------------------------------------------------------
 #define islog(a, b) (((a)>0 && (b)>10*(a)) || ((b)<0 && (a)<10*(b)))
 // NOTE: I use <=0 for proper tick labels rotation. But this mirror labels for central origin!
-#define sign(a)	((a)<=0 ? -1:1)
+#define sign(a)	((a)<0 ? -1:1)
 //-----------------------------------------------------------------------------
 MGL_NO_EXPORT inline struct tm *mgl_localtime (const time_t *clock, tm *result, bool use_utc)
 {	if (!clock || !result) return NULL;
@@ -52,12 +52,12 @@ void MGL_EXPORT mgl_wcstrim(wchar_t *str)
 //-----------------------------------------------------------------------------
 void mglCanvas::SetAxisStl(const char *stl, const char *tck, const char *sub)
 {
-	if(!stl || !(*stl))		strcpy(AxisStl,"k");
-	else if(strlen(stl)<32)	strcpy(AxisStl,stl);
-	if(!tck || !(*tck))		strcpy(TickStl,AxisStl);
-	else if(strlen(tck)<32)	strcpy(TickStl,tck);
-	if(!sub || !(*sub))		strcpy(SubTStl,TickStl);
-	else if(strlen(sub)<32)	strcpy(SubTStl,sub);
+	if(!stl || !(*stl))	strncpy(AxisStl,"k",32);
+	else 				strncpy(AxisStl,stl,32);
+	if(!tck || !(*tck))	strncpy(TickStl,AxisStl,32);
+	else 				strncpy(TickStl,tck,32);
+	if(!sub || !(*sub))	strncpy(SubTStl,TickStl,32);
+	else 				strncpy(SubTStl,sub,32);
 }
 //-----------------------------------------------------------------------------
 void mglCanvas::SetTickLen(mreal tlen, mreal stt)
@@ -227,7 +227,7 @@ void mglCanvas::SetTickTime(char dir, mreal d, const char *t)
 	}
 	if(d==0)	// try to select opimal step
 	{
-
+		// TODO add subticks for drawing
 		if(abs(t1.tm_year-t2.tm_year)>1)
 			d = 365.25*24*3600*mgl_adj_val(abs(t1.tm_year-t2.tm_year));	// number of second in year NOTE: improve it
 		// NOTE here should be months ... but it is too unregular ... so omit it now
@@ -242,10 +242,9 @@ void mglCanvas::SetTickTime(char dir, mreal d, const char *t)
 		{	d = mgl_adj_val(abs(t1.tm_sec-t2.tm_sec));	d = d>1?d:1;	}
 		else	// adjust msec. NOTE: this is not supported by localtime() !!!
 			d = mgl_adj_val(fabs(aa.v2-aa.v1));
-		aa.ds = 0;
 	}
 
-	aa.dv = d;	aa.f = 1;	aa.txt.clear();
+	aa.ds = 0;	aa.dv = d;	aa.f = 1;	aa.txt.clear();
 	if(mbstowcs(0,t,0)<256) mbstowcs(aa.t,t,256);
 
 	if(strchr("xyztuvw",aa.ch))
@@ -563,6 +562,7 @@ void mglCanvas::DrawLabels(mglAxis &aa)
 	{
 		if(kk[i]<0)	continue;	// should be never here?!
 		c = aa.txt[i].val;
+		if(get(MGL_NO_ORIGIN) && c==aa.v0)	continue;
 		if(c>aa.v1 && c<aa.v2 && i%k!=0)	continue;
 		p = o+d*c;	nn = (s-o)/(Max-Min);	ScalePoint(p,nn);
 		mglPnt &qq = Pnt[kk[i]];
@@ -753,9 +753,9 @@ void mglCanvas::Labelw(char dir, const wchar_t *text, mreal pos, const char *opt
 		else	t = Min.z*pow(Max.z/Min.z, (pos+1)/2);
 		p = mglPoint(x0,y0,t);	q = mglPoint(0,0,1);	shift += az.sh;
 	}
-	char font[64],ff[3]=":C";
+	char font[64],ff[3]=":C";	memset(font,0,64);
 	if(pos<-0.2)	ff[1]='L';	if(pos>0.2)	ff[1]='R';
-	strcpy(font,FontDef);	strcat(font,ff);
+	strncpy(font,FontDef,63);	strcat(font,ff);
 	long kk = AddPnt(p,-1,q,0,7);	ff[1]=0;
 	ff[0] = GetLabelPos(t, kk, *aa);	strcat(font,ff);
 	text_plot(kk,text,font,-1.4,0.35+shift);
diff --git a/src/base.cpp b/src/base.cpp
index eaf22cc..fab76bb 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -148,7 +148,7 @@ void mglBase::StartGroup(const char *name, int id)
 {
 	LightScale();
 	char buf[128];
-	sprintf(buf,"%s_%d",name,id);
+	snprintf(buf,128,"%s_%d",name,id);
 	StartAutoGroup(buf);
 }
 //-----------------------------------------------------------------------------
@@ -246,7 +246,7 @@ long mglBase::AddPnt(mglPoint p, mreal c, mglPoint n, mreal a, int scl)
 	txt.GetC(c,a,q);	// RGBA color
 
 	// add gap for texture coordinates for compatibility with OpenGL
-	const mreal gap = 0./512;
+	const mreal gap = 0./MGL_TEXTURE_COLOURS;
 	q.c = ci+(q.c-ci)*(1-2*gap)+gap;
 	q.t = q.t*(1-2*gap)+gap;
 	q.ta = q.t;
@@ -708,21 +708,17 @@ MGL_EXPORT mglColorID mglColorIds[31] = {{'k', mglColor(0,0,0)},
 	{' ', mglColor(-1,-1,-1)},	{0, mglColor(-1,-1,-1)}	// the last one MUST have id=0
 };
 //-----------------------------------------------------------------------------
-void mglColor::Set(mglColor c, float br)
+void MGL_EXPORT mgl_chrrgb(char p, float c[3])
 {
-	if(br<0)	br=0;	if(br>2.f)	br=2.f;
-	r = br<=1.f ? c.r*br : 1 - (1-c.r)*(2-br);
-	g = br<=1.f ? c.g*br : 1 - (1-c.g)*(2-br);
-	b = br<=1.f ? c.b*br : 1 - (1-c.b)*(2-br);
-	a = 1;
-}
-//-----------------------------------------------------------------------------
-void mglColor::Set(char p, float bright)
-{
-	Set(-1,-1,-1);
+	c[0]=c[1]=c[2]=-1;
 	for(long i=0; mglColorIds[i].id; i++)
 		if(mglColorIds[i].id==p)
-		{	Set(mglColorIds[i].col, bright);	break;	}
+		{
+			c[0]=mglColorIds[i].col.r;
+			c[1]=mglColorIds[i].col.g;
+			c[2]=mglColorIds[i].col.b;
+			break;
+		}
 }
 //-----------------------------------------------------------------------------
 void mglTexture::Set(const char *s, int smooth, mreal alpha)
@@ -882,7 +878,7 @@ mreal mglBase::AddTexture(mglColor c)
 			return i+j/255.;
 	// add new texture
 	mglTexture t;
-	for(i=0;i<514;i++)	t.col[i]=c;
+	for(i=0;i<MGL_TEXTURE_COLOURS;i++)	t.col[i]=c;
 	MGL_PUSH(Txt,t,mutexTxt);	return Txt.size()-1;
 }
 //-----------------------------------------------------------------------------
@@ -892,7 +888,7 @@ mreal mglBase::NextColor(long &id)
 {
 	long i=abs(id)/256, n=Txt[i].n, p=abs(id)&0xff;
 	if(id>=0)	{	p=(p+1)%n;	id = 256*i+p;	}
-	mglColor c = Txt[i].col[int(512*(p+0.5)/n)];
+	mglColor c = Txt[i].col[int(MGL_TEXTURE_COLOURS*(p+0.5)/n)];
 	mreal dif, dmin=1;
 	// try to find closest color
 	for(long j=0;mglColorIds[j].id;j++)	for(long k=1;k<10;k++)
@@ -1282,6 +1278,7 @@ void mglBase::ClearUnused()
 		if(p.type==2)	{	p.n2=used[p.n2]-1;	p.n3=used[p.n3]-1;	}
 		if(p.type==3)	{	p.n2=used[p.n2]-1;	p.n3=used[p.n3]-1;	p.n4=used[p.n4]-1;	}
 	}
+	delete []used;
 #if MGL_HAVE_PTHREAD
 	pthread_mutex_unlock(&mutexPnt);	pthread_mutex_unlock(&mutexPrm);
 #endif
diff --git a/src/base_cf.cpp b/src/base_cf.cpp
index 24e07d6..227cab2 100644
--- a/src/base_cf.cpp
+++ b/src/base_cf.cpp
@@ -86,8 +86,10 @@ void MGL_EXPORT mgl_set_range_dat(HMGL gr, char dir, HCDT a, int add)
 	else if(dir=='x')	gr->XRange(a,add);
 	else if(dir=='y')	gr->YRange(a,add);
 	else if(dir=='z')	gr->ZRange(a,add);	}
-void MGL_EXPORT mgl_set_ranges(HMGL gr, double x1, double y1, double z1, double x2, double y2, double z2)
-{	gr->SetRanges(x1,y1,z1,x2,y2,z2);	}
+void MGL_EXPORT mgl_set_ranges(HMGL gr, double x1, double x2, double y1, double y2, double z1, double z2)
+{	gr->SetRanges(x1,x2,y1,y2,z1,z2);	}
+void MGL_EXPORT mgl_set_auto_ranges(HMGL gr, double x1, double x2, double y1, double y2, double z1, double z2, double c1, double c2)
+{	gr->SetAutoRanges(x1,x2,y1,y2,z1,z2,c1,c2);	}
 void MGL_EXPORT mgl_set_func(HMGL gr, const char *EqX,const char *EqY,const char *EqZ,const char *EqA)
 {	gr->SetFunc(EqX,EqY,EqZ,EqA);	}
 void MGL_EXPORT mgl_set_coor(HMGL gr, int how)	{	gr->SetCoor(how);	}
@@ -128,8 +130,10 @@ void MGL_EXPORT mgl_set_range_val_(uintptr_t *gr, const char *dir, mreal *v1, mr
 {	mgl_set_range_val(_GR_,*dir,*v1,*v2);	}
 void MGL_EXPORT mgl_set_range_dat_(uintptr_t *gr, const char *dir, uintptr_t *a, int *add,int)
 {	mgl_set_range_dat(_GR_,*dir,_DA_(a),*add);	}
-void MGL_EXPORT mgl_set_ranges_(uintptr_t *gr, mreal *x1, mreal *y1, mreal *z1, mreal *x2, mreal *y2, mreal *z2)
-{	_GR_->SetRanges(*x1,*y1,*z1,*x2,*y2,*z2);	}
+void MGL_EXPORT mgl_set_ranges_(uintptr_t *gr, mreal *x1, mreal *x2, mreal *y1, mreal *y2, mreal *z1, mreal *z2)
+{	_GR_->SetRanges(*x1,*x2,*y1,*y2,*z1,*z2);	}
+void MGL_EXPORT mgl_set_auto_ranges_(uintptr_t *gr, mreal *x1, mreal *x2, mreal *y1, mreal *y2, mreal *z1, mreal *z2, mreal *c1, mreal *c2)
+{	_GR_->SetAutoRanges(*x1,*x2,*y1,*y2,*z1,*z2,*c1,*c2);	}
 void MGL_EXPORT mgl_set_func_(uintptr_t *gr, const char *EqX,const char *EqY,const char *EqZ,const char *EqA,int lx,int ly,int lz,int la)
 {
 	char *sx=new char[lx+1];	memcpy(sx,EqX,lx);	sx[lx]=0;
@@ -197,7 +201,7 @@ void MGL_EXPORT mgl_test_txt(const char *str, ...)
 		char buf[256];
 		va_list lst;
 		va_start(lst,str);
-		vsprintf(buf,str,lst);
+		vsnprintf(buf,256,str,lst);
 		va_end(lst);
 		printf("TEST: %s\n",buf);
 		fflush(stdout);
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 36da383..b69308d 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -301,7 +301,7 @@ mreal mglCanvas::GetOrgZ(char dir) const
 //-----------------------------------------------------------------------------
 //	Put primitives
 //-----------------------------------------------------------------------------
-#define MGL_MARK_PLOT	if(Quality&4)	mark_draw(p,type,size,&d);else	\
+#define MGL_MARK_PLOT	if(Quality&4)	mark_draw(Pnt[p],type,size,&d);else	\
 						{	mglPrim a;	a.w = pw;	a.s = size;	\
 							a.n1 = p;	a.n4 = type;	add_prim(a);	}
 void mglCanvas::mark_plot(long p, char type, mreal size)
@@ -322,13 +322,14 @@ void mglCanvas::mark_plot(long p, char type, mreal size)
 	else	{	MGL_MARK_PLOT	}
 }
 //-----------------------------------------------------------------------------
-#define MGL_LINE_PLOT	if(Quality&4)	line_draw(p1,p2,&dd);else	\
+#define MGL_LINE_PLOT	if(Quality&4)	line_draw(Pnt[p1],Pnt[p2],&dd);else	\
 						{	mglPrim a(1);	a.n3=PDef;	a.s = pPos;	\
 							a.n1 = p1;	a.n2 = p2;	a.w = pw;	add_prim(a);	}
 void mglCanvas::line_plot(long p1, long p2)
 {
 	if(PDef==0)	return;
 	if(p1<0 || p2<0 || mgl_isnan(Pnt[p1].x) || mgl_isnan(Pnt[p2].x))	return;
+	if(p1>p2)	{	long kk=p1;	p1=p2;	p2=kk;	}	// rearrange start/end for proper dashing
 	long pp1=p1,pp2=p2;
 	mreal pw = fabs(PenWidth)*sqrt(font_factor/400), d;
 	d = hypot(Pnt[p1].x-Pnt[p2].x, Pnt[p1].y-Pnt[p2].y);
@@ -344,7 +345,7 @@ void mglCanvas::line_plot(long p1, long p2)
 	pPos = fmod(pPos+d/pw/1.5, 16);
 }
 //-----------------------------------------------------------------------------
-#define MGL_TRIG_PLOT	if(Quality&4)	trig_draw(p1,p2,p3,true,&d);else	\
+#define MGL_TRIG_PLOT	if(Quality&4)	trig_draw(Pnt[p1],Pnt[p2],Pnt[p3],true,&d);else	\
 						{	mglPrim a(2);	a.n1 = p1;	a.n2 = p2;	\
 							a.n3 = p3;	add_prim(a);}
 void mglCanvas::trig_plot(long p1, long p2, long p3)
@@ -358,7 +359,7 @@ void mglCanvas::trig_plot(long p1, long p2, long p3)
 	else	{	MGL_TRIG_PLOT	}
 }
 //-----------------------------------------------------------------------------
-#define MGL_QUAD_PLOT	if(Quality&4)	quad_draw(p1,p2,p3,p4,&d);else	\
+#define MGL_QUAD_PLOT	if(Quality&4)	quad_draw(Pnt[p1],Pnt[p2],Pnt[p3],Pnt[p4],&d);else	\
 						{	mglPrim a(3);	a.n1 = p1;	a.n2 = p2;	\
 							a.n3 = p3;	a.n4 = p4;	add_prim(a);	}
 void mglCanvas::quad_plot(long p1, long p2, long p3, long p4)
@@ -428,7 +429,19 @@ pthread_mutex_lock(&mutexPtx);
 		mglColor mc(ch);
 		if(!ch)	mc = col<0 ? mglColor(char(0.5-col)):Txt[long(col)].GetC(col);
 
-//		if(!get(MGL_ENABLE_RTEXT))
+/*		for(long i=0;i<Prm.size();i++)	// try don't draw text if one present at this point
+		{
+			const mglPnt &t=Pnt[Prm[i].n1];
+			if(Prm[i].type==6 && t.x==q.x && t.y==q.y)
+			{
+#if MGL_HAVE_PTHREAD
+				pthread_mutex_unlock(&mutexPtx);
+#endif
+				Pop();
+				return 0;
+			}
+		}*/
+
 		mglPrim a(6);	a.n1 = p;
 		a.n2 = int(255*mc.r) + 256*(int(255*mc.g) + 256*int(255*mc.b));
 		mglText txt(text,font);
@@ -459,7 +472,7 @@ pthread_mutex_lock(&mutexPtx);
 	}
 	fsize *= fnt->Puts(text,font,col)/2;
 #if MGL_HAVE_PTHREAD
-pthread_mutex_unlock(&mutexPtx);
+	pthread_mutex_unlock(&mutexPtx);
 #endif
 	Pop();	return fsize;
 }
@@ -480,7 +493,7 @@ void mglCanvas::Glyph(mreal x, mreal y, mreal f, int s, long j, mreal col)
 	d.PDef = s;		d.pPos = a.s;
 	d.ObjId=ObjId;	d.PenWidth=a.w;
 	
-	if(Quality&4)	glyph_draw(&a,&d);
+	if(Quality&4)	glyph_draw(a,&d);
 	else	add_prim(a);
 }
 //-----------------------------------------------------------------------------
@@ -708,6 +721,7 @@ void mglCanvas::AddLight(int n, mglPoint r, mglPoint d, char col, mreal br, mrea
 //-----------------------------------------------------------------------------
 void mglCanvas::arrow_plot(long n1, long n2, char st)
 {
+	if(n1<0 || n2<0 || !strchr("AVKSDTIO",st))	return;
 	float ll = PenWidth*ArrowSize*0.35*font_factor;
 	if((Quality&3)==3)
 		arrow_plot_3d(n1, n2, st, ll);
diff --git a/src/canvas_cf.cpp b/src/canvas_cf.cpp
index e5f2cd7..4d673cb 100644
--- a/src/canvas_cf.cpp
+++ b/src/canvas_cf.cpp
@@ -20,6 +20,7 @@
 #include "mgl2/canvas.h"
 #include "mgl2/canvas_cf.h"
 #include "mgl2/eval.h"
+#include "mgl2/evalc.h"
 //-----------------------------------------------------------------------------
 #undef _GR_
 #define _GR_	((mglCanvas *)(*gr))
@@ -400,13 +401,6 @@ void MGL_EXPORT mgl_legend_(uintptr_t *gr, int *where, const char *font, const c
 void MGL_EXPORT mgl_set_legend_marks_(uintptr_t *gr, int *num)
 {	_GR_->SetLegendMarks(*num);	}
 //-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_mpi_send(HMGL gr, int id)
-{	mglCanvas *g = dynamic_cast<mglCanvas *>(gr);	if(g)	g->MPI_Send(id);	}
-void MGL_EXPORT mgl_mpi_recv(HMGL gr, int id)
-{	mglCanvas *g = dynamic_cast<mglCanvas *>(gr);	if(g)	g->MPI_Recv(id);	}
-void MGL_EXPORT mgl_mpi_send_(uintptr_t *gr, int *id)	{	mgl_mpi_send(_GR_, *id);	}
-void MGL_EXPORT mgl_mpi_recv_(uintptr_t *gr, int *id)	{	mgl_mpi_recv(_GR_, *id);	}
-//-----------------------------------------------------------------------------
 void MGL_EXPORT mgl_wnd_set_delay(HMGL gr, double dt)
 {	mglCanvas *g = dynamic_cast<mglCanvas *>(gr);	if(g)	g->SetDelay(dt);	}
 double MGL_EXPORT mgl_wnd_get_delay(HMGL gr)
@@ -435,6 +429,13 @@ double MGL_EXPORT mgl_eval_expr_(uintptr_t *ex, mreal *x, mreal *y, mreal *z)
 double MGL_EXPORT mgl_diff_expr_(uintptr_t *ex, const char *dir, mreal *x, mreal *y, mreal *z, int)
 {	return mgl_expr_diff((HMEX) ex, *dir,*x,*y,*z);	}
 //-----------------------------------------------------------------------------
+HAEX MGL_EXPORT mgl_create_cexpr(const char *expr)	{	return new mglFormulaC(expr);	}
+void MGL_EXPORT mgl_delete_cexpr(HAEX ex)	{	delete ex;	}
+dual MGL_EXPORT mgl_cexpr_eval(HAEX ex, dual x, dual y,dual z)
+{	return ex->Calc(x,y,z);	}
+dual MGL_EXPORT mgl_cexpr_eval_v(HAEX ex, dual *var)
+{	return ex->Calc(var);	}
+//-----------------------------------------------------------------------------
 void MGL_EXPORT mgl_set_plotfactor(HMGL gr, double val)
 {	mglCanvas *g = dynamic_cast<mglCanvas *>(gr);	if(g)	g->SetPlotFactor(val);	}
 void MGL_EXPORT mgl_set_plotfactor_(uintptr_t *gr, mreal *val)
diff --git a/src/complex.cpp b/src/complex.cpp
index b130f7f..f68cf15 100644
--- a/src/complex.cpp
+++ b/src/complex.cpp
@@ -1,5 +1,5 @@
 /***************************************************************************
- * data.cpp is part of Math Graphic Library
+ * complex.cpp is part of Math Graphic Library
  * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>       *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -19,14 +19,8 @@
  ***************************************************************************/
 #include "mgl2/datac.h"
 #include "mgl2/evalc.h"
-
-#if MGL_HAVE_GSL
-#include <gsl/gsl_fft_complex.h>
-#include <gsl/gsl_dht.h>
-#include <gsl/gsl_sf.h>
-#endif
 //-----------------------------------------------------------------------------
-void mglStartThreadC(void *(*func)(void *), void (*post)(mglThreadC *,dual *), long n,
+void MGL_EXPORT mglStartThreadC(void *(*func)(void *), void (*post)(mglThreadC *,dual *), long n,
 					dual *a, const dual *b, const dual *c, const long *p,
 					const void *v, const dual *d, const dual *e, const char *s)
 {
@@ -58,7 +52,7 @@ void mglStartThreadC(void *(*func)(void *), void (*post)(mglThreadC *,dual *), l
 	}
 }
 //-----------------------------------------------------------------------------
-void mglStartThreadV(void *(*func)(void *), long n, dual *a, const void *b,
+void MGL_EXPORT mglStartThreadV(void *(*func)(void *), long n, dual *a, const void *b,
 					const void *c, const long *p, const void *v, const mreal *d)
 {
 	if(!func)	return;
@@ -756,107 +750,3 @@ MGL_EXPORT dual *mgl_datac_value(HADT dat, long i,long j,long k)
 {	register long ii=i*dat->nx*(j+dat->ny*k);
 	return	ii>=0 && ii<dat->GetNN() ? dat->a+ii : 0;	}
 //-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_datac_fft(HADT d, const char *dir)
-{
-#if MGL_HAVE_GSL
-	if(!dir || *dir==0)	return;
-	long nx = d->nx, ny = d->ny, nz = d->nz;
-	double *a = new double[2*nx*ny*nz];
-	register long i,j;
-	gsl_fft_direction how = strchr(dir,'i')?backward:forward;
-	for(i=0;i<nx*ny*nz;i++)
-	{	a[2*i] = real(d->a[i]);	a[2*i+1] = imag(d->a[i]);	}
-	if(strchr(dir,'x') && nx>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(nx);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(nx);
-		for(i=0;i<ny*nz;i++)
-			gsl_fft_complex_transform(a+2*i*nx, 1, nx, wt, ws, how);
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	if(strchr(dir,'y') && ny>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(ny);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(ny);
-		for(j=0;j<nz;j++)	for(i=0;i<nx;i++)
-			gsl_fft_complex_transform(a+2*i+2*j*nx*ny, nx, ny, wt, ws, how);
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	if(strchr(dir,'z') && nz>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(nz);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(nz);
-		for(i=0;i<ny*nx;i++)
-			gsl_fft_complex_transform(a+2*i, nx*ny, nz, wt, ws, how);
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	for(i=0;i<nx*ny*nz;i++)	d->a[i] = dual(a[2*i], a[2*i+1]);
-	delete []a;
-#endif
-}
-void MGL_EXPORT mgl_datac_fft_(uintptr_t *d, const char *dir, int l)
-{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
-	mgl_datac_fft(_DC_,s);	delete []s;	}
-//-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_datac_hankel(HADT d, const char *dir)
-{
-	#if MGL_HAVE_GSL
-	if(!dir || *dir==0)	return;
-	double *ai=0, *af=0, *ag=0;
-	mreal mm;
-	gsl_dht *dht=0;
-	register long i,j,k;
-	long nx=d->nx, ny=d->ny, nz=d->nz;
-	dual *a=d->a;
-	if(strchr(dir,'x') && nx>1)
-	{
-		ai = new double[nx];	af = new double[nx];	ag = new double[nx];
-		dht = gsl_dht_new(nx,0,1);
-		mm = gsl_sf_bessel_zero_J0(nx+1);
-		for(i=0;i<ny*nz;i++)
-		{
-			for(j=0;j<nx;j++)	ai[j] = real(a[j+nx*i]);
-			gsl_dht_apply(dht,ai,af);
-			for(j=0;j<nx;j++)	ai[j] = imag(a[j+nx*i]);
-			gsl_dht_apply(dht,ai,ag);
-			for(j=0;j<nx;j++)	a[j+nx*i] = dual(af[j],ag[j])*mm;
-		}
-	}
-	if(strchr(dir,'y') && ny>1)
-	{
-		ai = new double[ny];	af = new double[ny];	ag = new double[ny];
-		dht = gsl_dht_new(ny,0,1);
-		mm = gsl_sf_bessel_zero_J0(ny+1);
-		for(i=0;i<nx;i++)	for(k=0;k<nz;k++)
-		{
-			for(j=0;j<nx;j++)	ai[j] = real(a[i+nx*(j+ny*k)]);
-			gsl_dht_apply(dht,ai,af);
-			for(j=0;j<nx;j++)	ai[j] = imag(a[i+nx*(j+ny*k)]);
-			gsl_dht_apply(dht,ai,ag);
-			for(j=0;j<nx;j++)	a[i+nx*(j+ny*k)] = dual(af[j],ag[j])*mm;
-		}
-	}
-	if(strchr(dir,'z') && nz>1)
-	{
-		ai = new double[nz];	af = new double[nz];	ag = new double[nz];
-		dht = gsl_dht_new(nz,0,1);
-		mm = gsl_sf_bessel_zero_J0(nz+1);
-		k = nx*ny;	for(i=0;i<k;i++)
-		{
-			for(j=0;j<nz;j++)	ai[j] = real(a[i+j*k]);
-			gsl_dht_apply(dht,ai,af);
-			for(j=0;j<nz;j++)	ai[j] = imag(a[i+j*k]);
-			gsl_dht_apply(dht,ai,ag);
-			for(j=0;j<nz;j++)	a[i+j*k] = dual(af[j],ag[j])*mm;
-		}
-	}
-	if(ai)	{	delete []ai;	delete []af;	gsl_dht_free(dht);	}
-	#endif
-}
-void MGL_EXPORT mgl_datac_hankel_(uintptr_t *d, const char *dir,int l)
-{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
-	mgl_datac_hankel(_DC_,s);	delete []s;	}
-//-----------------------------------------------------------------------------
diff --git a/src/complex_io.cpp b/src/complex_io.cpp
index bb9f5f7..09732c3 100644
--- a/src/complex_io.cpp
+++ b/src/complex_io.cpp
@@ -554,10 +554,10 @@ void MGL_EXPORT mgl_datac_modify_(uintptr_t *d, const char *eq,int *dim,int l)
 //-----------------------------------------------------------------------------
 MGL_NO_EXPORT void *mgl_cmodify_gen(void *par)
 {
-	mglThreadC *t=(mglThreadC *)par;
+	mglThreadV *t=(mglThreadV *)par;
 	const mglFormulaC *f = (const mglFormulaC *)(t->v);
 	register long i,j,k,i0, nx=t->p[0],ny=t->p[1],nz=t->p[2];
-	dual *b=t->a;
+	dual *b=t->aa;
 	mreal dx,dy,dz;
 	HCDT v=(HCDT)t->b, w=(HCDT)t->c;
 	dx=nx>1?1/(nx-1.):0;	dy=ny>1?1/(ny-1.):0;	dz=nz>1?1/(nz-1.):0;
@@ -577,7 +577,7 @@ void MGL_EXPORT mgl_datac_modify_vw(HADT d, const char *eq,HCDT vdat,HCDT wdat)
 	if(wdat && wdat->GetNN()!=nn)	return;
 	mglFormulaC f(eq);
 	if(v && w)	mglStartThreadC(mgl_cmodify,0,nn,d->a,v->a,w->a,par,&f);
-	else if(vdat && wdat)	mglStartThreadV(mgl_cmodify_gen,nn,d->a,vdat,wdat,par,&f);
+	else if(vdat && wdat)	(mgl_cmodify_gen,nn,d->a,vdat,wdat,par,&f);
 	else if(v)	mglStartThreadC(mgl_cmodify,0,nn,d->a,v->a,0,par,&f);
 	else if(vdat)	mglStartThreadV(mgl_cmodify_gen,nn,d->a,vdat,0,par,&f);
 	else	mglStartThreadC(mgl_cmodify,0,nn,d->a,0,0,par,&f);
@@ -603,16 +603,16 @@ MGL_NO_EXPORT void *mgl_cfill_f(void *par)
 }
 MGL_NO_EXPORT void *mgl_cfill_fgen(void *par)
 {
-	mglThreadC *t=(mglThreadC *)par;
+	mglThreadV *t=(mglThreadV *)par;
 	const mglFormulaC *f = (const mglFormulaC *)(t->v);
 	register long i,j,k,i0, nx=t->p[0],ny=t->p[1];
-	dual *b=t->a;
+	dual *b=t->aa;
 	HCDT v=(HCDT)t->b, w=(HCDT)t->c;
-	const dual *x=t->d;
+	const mreal *x=t->d;
 	for(i0=t->id;i0<t->n;i0+=mglNumThr)
 	{
 		i=i0%nx;	j=((i0/nx)%ny);	k=i0/(nx*ny);
-		b[i0] = f->Calc(x[0]+mreal(i)*x[1], x[2]+mreal(j)*x[3], x[4]+mreal(k)*x[5],
+		b[i0] = f->Calc(x[0]+i*x[1], x[2]+j*x[3], x[4]+k*x[5],
 						b[i0], v?v->vthr(i0):0, w?w->vthr(i0):0);
 	}
 	return 0;
@@ -686,11 +686,11 @@ int MGL_EXPORT mgl_datac_read_range(HADT dat, const char *templ, double from, do
 	mglDataC d;
 	double t = from;
 	dual *b;
-	long kx,ky,kz;
-	char *fname = new char[strlen(templ)+20];
+	long kx,ky,kz,n=strlen(templ)+20;
+	char *fname = new char[n];
 
 	//read first file
-	do{	sprintf(fname,templ,t);	t+= step;	} while(!mgl_datac_read(&d,fname) && t<=to);
+	do{	snprintf(fname,n,templ,t);	t+= step;	} while(!mgl_datac_read(&d,fname) && t<=to);
 
 	if(t>to)	return false;
 	kx = d.nx;	ky = d.ny;	kz = d.nz;
@@ -700,7 +700,7 @@ int MGL_EXPORT mgl_datac_read_range(HADT dat, const char *templ, double from, do
 	// read other files
 	for(;t<=to;t+=step)
 	{
-		sprintf(fname,templ,t);
+		snprintf(fname,n,templ,t);
 		if(mgl_datac_read(&d,fname))
 			if(!mgl_add_file(kx,ky,kz,b,&d,as_slice))
 			{	delete []fname;	free(b);	return false;	}
diff --git a/src/data.cpp b/src/data.cpp
index 03461f6..84693df 100644
--- a/src/data.cpp
+++ b/src/data.cpp
@@ -55,7 +55,7 @@ void MGL_EXPORT mgl_set_num_thr(int n)
 void MGL_EXPORT mgl_set_num_thr(int)	{	mglNumThr = 1;	}
 #endif
 //-----------------------------------------------------------------------------
-void mglStartThread(void *(*func)(void *), void (*post)(mglThreadD *,mreal *), long n,
+void MGL_EXPORT mglStartThread(void *(*func)(void *), void (*post)(mglThreadD *,mreal *), long n,
 					mreal *a, const mreal *b, const mreal *c, const long *p,
 					const void *v, const mreal *d, const mreal *e, const char *s)
 {
@@ -87,7 +87,7 @@ void mglStartThread(void *(*func)(void *), void (*post)(mglThreadD *,mreal *), l
 	}
 }
 //-----------------------------------------------------------------------------
-void mglStartThreadV(void *(*func)(void *), long n, mreal *a, const void *b,
+void MGL_EXPORT mglStartThreadV(void *(*func)(void *), long n, mreal *a, const void *b,
 					 const void *c, const long *p, const void *v, const mreal *d)
 {
 	if(!func)	return;
@@ -127,7 +127,7 @@ double mgl_ipow(double x,int n)
 	if(n%2==1)	t *= x;
 	return t;
 }
-double mgl_ipow_(double *x,int *n)	{	return mgl_ipow(*x,*n);	}
+double mgl_ipow_(mreal *x,int *n)	{	return mgl_ipow(*x,*n);	}
 //-----------------------------------------------------------------------------
 double mgl_get_time(const char *time, const char *fmt)
 {
@@ -1457,19 +1457,19 @@ MGL_EXPORT const char *mgl_data_info(HCDT d)	// NOTE: Not thread safe function!
 {
 	static char buf[512];
 	char s[128];	buf[0]=0;
-	sprintf(s,"nx = %ld\tny = %ld\tnz = %ld\n",d->GetNx(),d->GetNy(),d->GetNz());	strcat(buf,s);
+	snprintf(s,128,"nx = %ld\tny = %ld\tnz = %ld\n",d->GetNx(),d->GetNy(),d->GetNz());	strcat(buf,s);
 
 	long i=0,j=0,k=0;
 	mreal A=0,Wa=0,X=0,Y=0,Z=0,Wx=0,Wy=0,Wz=0, b;
 	b = mgl_data_max_int(d,&i,&j,&k);
-	sprintf(s,"Maximum is %g\t at x = %ld\ty = %ld\tz = %ld\n", b,i,j,k);	strcat(buf,s);
+	snprintf(s,128,"Maximum is %g\t at x = %ld\ty = %ld\tz = %ld\n", b,i,j,k);	strcat(buf,s);
 	b = mgl_data_min_int(d,&i,&j,&k);
-	sprintf(s,"Minimum is %g\t at x = %ld\ty = %ld\tz = %ld\n", b,i,j,k);	strcat(buf,s);
+	snprintf(s,128,"Minimum is %g\t at x = %ld\ty = %ld\tz = %ld\n", b,i,j,k);	strcat(buf,s);
 
 	mgl_data_momentum_val(d,'a',&A,&Wa,0,0);	mgl_data_momentum_val(d,'x',&X,&Wx,0,0);
 	mgl_data_momentum_val(d,'y',&Y,&Wy,0,0);	mgl_data_momentum_val(d,'z',&Z,&Wz,0,0);
-	sprintf(s,"Averages are:\n<a> = %g\t<x> = %g\t<y> = %g\t<z> = %g\n", A,X,Y,Z);	strcat(buf,s);
-	sprintf(s,"Widths are:\nWa = %g\tWx = %g\tWy = %g\tWz = %g\n", Wa,Wx,Wy,Wz);	strcat(buf,s);
+	snprintf(s,128,"Averages are:\n<a> = %g\t<x> = %g\t<y> = %g\t<z> = %g\n", A,X,Y,Z);	strcat(buf,s);
+	snprintf(s,128,"Widths are:\nWa = %g\tWx = %g\tWy = %g\tWz = %g\n", Wa,Wx,Wy,Wz);	strcat(buf,s);
 	return buf;
 }
 //-----------------------------------------------------------------------------
diff --git a/src/data_io.cpp b/src/data_io.cpp
index b663cfa..f7107ea 100644
--- a/src/data_io.cpp
+++ b/src/data_io.cpp
@@ -24,6 +24,7 @@
 #endif
 
 #include "mgl2/data.h"
+#include "mgl2/datac.h"
 #include "mgl2/eval.h"
 
 #if MGL_HAVE_HDF5
@@ -82,7 +83,7 @@ void mglFromStr(HMDT d,char *buf,long NX,long NY,long NZ)	// TODO: add multithre
 		char *s=buf+j;
 		while(buf[j]>' ' && buf[j]!=',' && buf[j]!=';' && j<nb)	j++;
 		buf[j]=0;
-		d->a[i] = atof(s);
+		d->a[i] = strstr(s,"NAN")?NAN:atof(s);
 		i++;	if(i>=NX*NY*NZ)	break;
 	}
 }
@@ -248,6 +249,10 @@ void MGL_EXPORT mgl_data_save(HCDT d, const char *fname,long ns)
 	long nx=d->GetNx(), ny=d->GetNy(), nz=d->GetNz();
 	if(ns<0 || (ns>=nz && nz>1))	for(k=0;k<nz;k++)
 	{	// save whole data
+		const mglData *dr = dynamic_cast<const mglData *>(d);
+		if(dr)	fprintf(fp,"## %s\n",dr->id.c_str());
+		const mglDataC *dc = dynamic_cast<const mglDataC *>(d);
+		if(dc)	fprintf(fp,"## %s\n",dc->id.c_str());
 		for(i=0;i<ny;i++)
 		{
 			for(j=0;j<nx-1;j++)	fprintf(fp,"%g\t",d->v(j,i,k));
@@ -812,7 +817,7 @@ MGL_NO_EXPORT void *mgl_fill_f(void *par)
 }
 MGL_NO_EXPORT void *mgl_fill_fgen(void *par)
 {
-	mglThreadD *t=(mglThreadD *)par;
+	mglThreadV *t=(mglThreadV *)par;
 	const mglFormula *f = (const mglFormula *)(t->v);
 	register long i,j,k,i0, nx=t->p[0],ny=t->p[1];
 	mreal *b=t->a;
@@ -1034,11 +1039,11 @@ int MGL_EXPORT mgl_data_read_range(HMDT dat, const char *templ, double from, dou
 {
 	mglData d;
 	mreal t = from, *b;
-	long kx,ky,kz;
-	char *fname = new char[strlen(templ)+20];
+	long kx,ky,kz,n=strlen(templ)+20;
+	char *fname = new char[n];
 
 	//read first file
-	do{	sprintf(fname,templ,t);	t+= step;	} while(!mgl_data_read(&d,fname) && t<=to);
+	do{	snprintf(fname,n,templ,t);	t+= step;	} while(!mgl_data_read(&d,fname) && t<=to);
 
 	if(t>to)	return false;
 	kx = d.nx;	ky = d.ny;	kz = d.nz;
@@ -1048,7 +1053,7 @@ int MGL_EXPORT mgl_data_read_range(HMDT dat, const char *templ, double from, dou
 	// read other files
 	for(;t<=to;t+=step)
 	{
-		sprintf(fname,templ,t);
+		snprintf(fname,n,templ,t);
 		if(mgl_data_read(&d,fname))
 			if(!mgl_add_file(kx,ky,kz,b,&d,as_slice))
 			{	delete []fname;	free(b);	return false;	}
diff --git a/src/data_new.cpp b/src/data_new.cpp
index 2841ffb..3dcc0e0 100644
--- a/src/data_new.cpp
+++ b/src/data_new.cpp
@@ -20,71 +20,6 @@
 #include <ctype.h>
 #include "mgl2/data.h"
 #include "mgl2/eval.h"
-
-#if MGL_HAVE_GSL
-#include <gsl/gsl_fft_complex.h>
-#include <gsl/gsl_dht.h>
-#include <gsl/gsl_sf.h>
-#endif
-//-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_data_envelop(HMDT d, char dir)
-{
-#if MGL_HAVE_GSL
-	register long i,j,k,i0;
-	long nx=d->nx,ny=d->ny,nz=d->nz,nn=nx*ny*nz;
-	double *b = new double[2*nn];
-	mreal *a=d->a;
-	for(i=0;i<nn;i++)	{	b[2*i] = a[i];	b[2*i+1] = 0;	}
-	if(dir=='x' && nx>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(nx);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(nx);
-		for(i=0;i<ny*nz;i++)
-		{
-			gsl_fft_complex_transform(b+2*i*nx, 1, nx, wt, ws, forward);
-			for(j=0;j<nx;j++)
-			{	b[j+2*i*nx] /= nx/2.;	b[j+nx+2*i*nx] = 0;	}
-			gsl_fft_complex_transform(b+2*i*nx, 1, nx, wt, ws, backward);
-		}
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	if(dir=='y' && ny>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(ny);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(ny);
-		for(j=0;j<nz;j++)	for(i=0;i<nx;i++)
-		{
-			i0 = 2*i+2*j*nx*ny;
-			gsl_fft_complex_transform(b+i0, nx, ny, wt, ws, forward);
-			for(k=0;k<ny;k++)
-			{	b[i0+k*2*nx] /= ny/2.;	b[i0+2*nx*k+2*nx*ny] = 0;	}
-			gsl_fft_complex_transform(b+i0, nx, ny, wt, ws, backward);
-		}
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	if(dir=='z' && nz>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(nz);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(nz);
-		for(i=0;i<ny*nx;i++)
-		{
-			i0 = 2*nx*ny;
-			gsl_fft_complex_transform(b+2*i, nx*ny, nz, wt, ws, forward);
-			for(j=0;j<nz;j++)
-			{	b[i+j*i0] /= nz/2.;	b[i+j*i0+nz*i0] = 0;	}
-			gsl_fft_complex_transform(b+2*i, nx*ny, nz, wt, ws, backward);
-		}
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	for(i=0;i<nx*ny*nz;i++)	a[i] = hypot(b[2*i], b[2*i+1]);
-	delete []b;
-#endif
-}
-void MGL_EXPORT mgl_data_envelop_(uintptr_t *d, const char *dir, int)
-{	mgl_data_envelop(_DT_,*dir);	}
 //-----------------------------------------------------------------------------
 HMDT MGL_EXPORT mgl_data_trace(HCDT d)
 {
@@ -640,412 +575,6 @@ HMDT MGL_EXPORT mgl_data_evaluate(HCDT dat, HCDT idat, HCDT jdat, HCDT kdat, int
 uintptr_t MGL_EXPORT mgl_data_evaluate_(uintptr_t *d, uintptr_t *idat, uintptr_t *jdat, uintptr_t *kdat, int *norm)
 {	return uintptr_t(mgl_data_evaluate(_DT_,_DA_(idat),_DA_(jdat),_DA_(kdat),*norm));	}
 //-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_data_fourier(HMDT re, HMDT im, const char *dir)
-{
-#if MGL_HAVE_GSL
-	if(!dir || *dir==0)	return;
-	long nx = re->nx, ny = re->ny, nz = re->nz;
-	if(nx*ny*nz != im->nx*im->ny*im->nz || !dir || dir[0]==0)	return;
-	double *a = new double[2*nx*ny*nz];
-	register long i,j;
-	for(i=0;i<nx*ny*nz;i++)
-	{	a[2*i] = re->a[i];	a[2*i+1] = im->a[i];	}
-	if(strchr(dir,'x') && nx>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(nx);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(nx);
-		if(strchr(dir,'i'))
-			for(i=0;i<ny*nz;i++)	gsl_fft_complex_inverse(a+2*i*nx, 1, nx, wt, ws);
-		else
-			for(i=0;i<ny*nz;i++)	gsl_fft_complex_forward(a+2*i*nx, 1, nx, wt, ws);
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	if(strchr(dir,'y') && ny>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(ny);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(ny);
-		if(strchr(dir,'i'))
-			for(j=0;j<nz;j++)	for(i=0;i<nx;i++)
-				gsl_fft_complex_inverse(a+2*i+2*j*nx*ny, nx, ny, wt, ws);
-		else
-			for(j=0;j<nz;j++)	for(i=0;i<nx;i++)
-				gsl_fft_complex_forward(a+2*i+2*j*nx*ny, nx, ny, wt, ws);
-
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	if(strchr(dir,'z') && nz>1)
-	{
-		gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(nz);
-		gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(nz);
-		if(strchr(dir,'i'))
-			for(i=0;i<ny*nx;i++)	gsl_fft_complex_inverse(a+2*i, nx*ny, nz, wt, ws);
-		else
-			for(i=0;i<ny*nx;i++)	gsl_fft_complex_forward(a+2*i, nx*ny, nz, wt, ws);
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);
-	}
-	for(i=0;i<nx*ny*nz;i++)
-	{	re->a[i] = a[2*i];	im->a[i] = a[2*i+1];	}
-	delete []a;
-#endif
-}
-void MGL_EXPORT mgl_data_fourier_(uintptr_t *re, uintptr_t *im, const char *dir, int l)
-{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
-	mgl_data_fourier(_DM_(re),_DM_(im),s);	delete []s;	}
-//-----------------------------------------------------------------------------
-HMDT MGL_EXPORT mgl_data_stfa(HCDT re, HCDT im, long dn, char dir)
-{
-	mglData *d=new mglData;
-#if MGL_HAVE_GSL
-	if(dn<2)	return d;
-	dn = 2*(dn/2);
-	long nx = re->GetNx(), ny = re->GetNy();
-	if(nx*ny!=im->GetNx()*im->GetNy())	return d;
-	register long i,j,k,i0,dd=dn/2;
-	double *a = new double[4*dn],ff;
-//	for(i=0;i<nx*ny;i++)	{	a[2*i] = re.a[i];	a[2*i+1] = im.a[i];	}
-	gsl_fft_complex_wavetable *wt = gsl_fft_complex_wavetable_alloc(2*dn);
-	gsl_fft_complex_workspace *ws = gsl_fft_complex_workspace_alloc(2*dn);
-	long mx,my,mz;
-	if(dir=='y')
-	{
-		mx = nx;	my = dn;	mz = ny/dn;
-		mgl_data_create(d, mx, mz, my);
-		for(j=0;j<mz;j++)	for(i=0;i<mx;i++)
-		{
-			for(k=0;k<2*dn;k++)
-			{
-				i0 = k-dd+j*dn;		ff = 1;
-				if(i0<0)	i0=0;	else if(i0>=ny)	i0=ny-1;
-				if(k<dd)
-				{	ff = 0.5*(k-dd/2.)/dd;		ff=0.5+ff*(3-ff*ff);	}
-				else if(k>=dn+dd)
-				{	ff = 0.5*(k-3.5*dd)/dd;	ff=0.5-ff*(3-ff*ff);	}
-				a[2*k] = re->v(i,i0)*ff;	a[2*k+1] = im->v(i,i0)*ff;
-			}
-			gsl_fft_complex_forward(a, 1, 2*dn, wt, ws);
-			for(k=0;k<dd;k++)
-			{
-				i0 = i+mx*(j+mz*k);
-				d->a[i0+mx*mz*dd] = hypot(a[4*k],a[4*k+1])/dn;
-				d->a[i0] = hypot(a[4*k+2*dn],a[4*k+2*dn+1])/dn;
-			}
-		}
-	}
-	else
-	{
-		mx = dn;	my = nx/dn;	mz = ny;
-		mgl_data_create(d, my, mx, mz);
-		for(j=0;j<mz;j++)	for(i=0;i<my;i++)
-		{
-			for(k=0;k<2*dn;k++)
-			{
-				i0 = k-dd+i*dn;		ff = 1;
-				if(i0<0)	i0=0;	else if(i0>=nx)	i0=nx-1;
-				if(k<dd)
-				{	ff = 0.5*(k-dd/2.)/dd;	ff=0.5+ff*(3-ff*ff);	}
-				else if(k>=3*dd)
-				{	ff = 0.5*(k-3.5*dd)/dd;	ff=0.5-ff*(3-ff*ff);	}
-				a[2*k] = re->v(i0,j)*ff;	a[2*k+1] = im->v(i0,j)*ff;
-			}
-			gsl_fft_complex_forward(a, 1, 2*dn, wt, ws);
-			for(k=0;k<dd;k++)
-			{
-				i0 = i+my*(k+mx*j);
-				d->a[i0+dd*my] = hypot(a[4*k],a[4*k+1])/dn;
-				d->a[i0] = hypot(a[4*k+2*dn],a[4*k+2*dn+1])/dn;
-			}
-		}
-	}
-	delete []a;
-	gsl_fft_complex_workspace_free(ws);
-	gsl_fft_complex_wavetable_free(wt);
-#endif
-	return d;
-}
-uintptr_t MGL_EXPORT mgl_data_stfa_(uintptr_t *re, uintptr_t *im, int *dn, char *dir, int)
-{	return uintptr_t(mgl_data_stfa(_DA_(re),_DA_(im),*dn,*dir));	}
-//-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_data_fill_sample(HMDT d, const char *how)
-{
-	if(!how || *how==0)	return;
-	bool xx = strchr(how,'x');
-	register long i,n=d->nx;
-	mreal *aa=d->a;
-	if(strchr(how,'h'))	// Hankel
-	{
-#if MGL_HAVE_GSL
-		gsl_dht *dht = gsl_dht_new(n,0,1);
-		for(i=0;i<n;i++)
-			aa[i] = xx ? gsl_dht_x_sample(dht, i) : gsl_dht_k_sample(dht, i);
-		gsl_dht_free(dht);
-#endif
-	}
-	else	// Fourier
-	{
-		if(xx)	for(i=0;i<n;i++)	aa[i] = mreal(2*i-n)/n;
-		else	for(i=0;i<n;i++)	aa[i] = M_PI*(i<n/2 ? i:i-n);
-	}
-	for(i=1;i<d->ny*d->nz;i++)	memcpy(aa+i*n,aa,n*sizeof(mreal));
-}
-void MGL_EXPORT mgl_data_fill_sample_(uintptr_t *d, const char *how,int l)
-{	char *s=new char[l+1];	memcpy(s,how,l);	s[l]=0;
-	mgl_data_fill_sample(_DT_,s);	delete []s;	}
-//-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_data_hankel(HMDT d, const char *dir)
-{
-#if MGL_HAVE_GSL
-	if(!dir || *dir==0)	return;
-	double *ai=0, *af=0, mm;
-	gsl_dht *dht=0;
-	register long i,j,k;
-	long nx=d->nx, ny=d->ny, nz=d->nz;
-	mreal *a=d->a;
-	if(strchr(dir,'x') && nx>1)
-	{
-		ai = new double[nx];	af = new double[nx];
-		dht = gsl_dht_new(nx,0,1);
-		mm = gsl_sf_bessel_zero_J0(nx+1);
-		for(i=0;i<ny*nz;i++)
-		{
-			for(j=0;j<nx;j++)	ai[j] = a[j+nx*i];
-			gsl_dht_apply(dht,ai,af);
-			for(j=0;j<nx;j++)	a[j+nx*i] = af[j]*mm;
-		}
-	}
-	if(strchr(dir,'y') && ny>1)
-	{
-		ai = new double[ny];	af = new double[ny];
-		dht = gsl_dht_new(ny,0,1);
-		mm = gsl_sf_bessel_zero_J0(ny+1);
-		for(i=0;i<nx;i++)	for(k=0;k<nz;k++)
-		{
-			for(j=0;j<nx;j++)	ai[j] = a[i+nx*(j+ny*k)];
-			gsl_dht_apply(dht,ai,af);
-			for(j=0;j<nx;j++)	a[i+nx*(j+ny*k)] = af[j]*mm;
-		}
-	}
-	if(strchr(dir,'z') && nz>1)
-	{
-		ai = new double[nz];	af = new double[nz];
-		dht = gsl_dht_new(nz,0,1);
-		mm = gsl_sf_bessel_zero_J0(nz+1);
-		k = nx*ny;	for(i=0;i<k;i++)
-		{
-			for(j=0;j<nz;j++)	ai[j] = a[i+j*k];
-			gsl_dht_apply(dht,ai,af);
-			for(j=0;j<nz;j++)	a[i+j*k] = af[j]*mm;
-		}
-	}
-	if(ai)	{	delete []ai;	delete []af;	gsl_dht_free(dht);	}
-#endif
-}
-void MGL_EXPORT mgl_data_hankel_(uintptr_t *d, const char *dir,int l)
-{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
-	mgl_data_hankel(_DT_,s);	delete []s;	}
-//-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_data_cosfft(HMDT d, const char *dir)
-{
-#if MGL_HAVE_GSL
-	if(!dir || *dir==0)	return;
-	double *b = 0;
-	gsl_fft_complex_wavetable *wt=0;
-	gsl_fft_complex_workspace *ws=0;
-	register long i,j,k;
-	long nx=d->nx, ny=d->ny, nz=d->nz;
-	mreal *a=d->a;
-	if(strchr(dir,'x') && nx>1)
-	{
-		wt = gsl_fft_complex_wavetable_alloc(2*nx);
-		ws = gsl_fft_complex_workspace_alloc(2*nx);
-		b = new double[4*nx];
-		for(i=0;i<ny*nz;i++)
-		{
-			k = i*nx;	memset(b,0,4*nx*sizeof(double));	b[0] = b[2*nx] = a[k];
-			for(j=1;j<nx;j++)	{	b[2*j] = a[k+j];	b[4*nx-2*j] = a[k+j];	}
-			gsl_fft_complex_transform(b, 1, 2*nx, wt, ws, forward);
-			for(j=0;j<nx;j++)	a[k+j] = b[2*j]/sqrt(2.*nx);
-		}
-	}
-	if(strchr(dir,'y') && ny>1)
-	{
-		wt = gsl_fft_complex_wavetable_alloc(2*ny);
-		ws = gsl_fft_complex_workspace_alloc(2*ny);
-		b = new double[4*ny];
-		for(i=0;i<nx;i++)	for(k=0;k<nz;k++)
-		{
-			memset(b,0,4*ny*sizeof(double));	b[0] = b[2*ny] = a[i+nx*ny*k];
-			for(j=1;j<ny;j++)	{	b[2*j] = a[i+nx*(ny*k+j)];	b[4*ny-2*j] = a[i+nx*(ny*k+j)];	}
-			gsl_fft_complex_transform(b, 1, 2*ny, wt, ws, forward);
-			for(j=0;j<ny;j++)	a[i+nx*(ny*k+j)] = b[2*j]/sqrt(2.*ny);
-		}
-	}
-	if(strchr(dir,'z') && nz>1)
-	{
-		wt = gsl_fft_complex_wavetable_alloc(2*nz);
-		ws = gsl_fft_complex_workspace_alloc(2*nz);
-		b = new double[4*nz];	k = nx*ny;
-		for(i=0;i<k;i++)
-		{
-			memset(b,0,4*nz*sizeof(double));	b[0] = b[2*nx] = a[i];
-			for(j=1;j<nx;j++)	{	b[2*j] = a[i+k*j];	b[4*nx-2*j] = a[i+k*j];	}
-			gsl_fft_complex_transform(b, 1, 2*nz, wt, ws, forward);
-			for(j=0;j<nz;j++)	a[i+k*j] = b[2*j]/sqrt(2.*nz);
-		}
-	}
-	if(b)
-	{	delete []b;
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);	}
-#endif
-}
-void MGL_EXPORT mgl_data_cosfft_(uintptr_t *d, const char *dir,int l)
-{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
-	mgl_data_cosfft(_DT_,s);	delete []s;	}
-//-----------------------------------------------------------------------------
-void MGL_EXPORT mgl_data_sinfft(HMDT d, const char *dir)
-{
-#if MGL_HAVE_GSL
-	if(!dir || *dir==0)	return;
-	double *b = 0;
-	gsl_fft_complex_wavetable *wt=0;
-	gsl_fft_complex_workspace *ws=0;
-	register long i,j,k;
-	long nx=d->nx, ny=d->ny, nz=d->nz;
-	mreal *a=d->a;
-	if(strchr(dir,'x') && nx>1)
-	{
-		wt = gsl_fft_complex_wavetable_alloc(2*nx);
-		ws = gsl_fft_complex_workspace_alloc(2*nx);
-		b = new double[4*nx];
-		for(i=0;i<ny*nz;i++)
-		{
-			k = i*nx;	memset(b,0,4*nx*sizeof(double));	b[0] = a[k];	b[2*nx] = -a[k];
-			for(j=1;j<nx;j++)	{	b[2*j] = a[k+j];	b[4*nx-2*j] = -a[k+j];	}
-			gsl_fft_complex_transform(b, 1, 2*nx, wt, ws, forward);
-			for(j=0;j<nx;j++)	a[k+j] = -b[2*j+1]/sqrt(2.*nx);
-		}
-	}
-	if(strchr(dir,'y') && ny>1)
-	{
-		wt = gsl_fft_complex_wavetable_alloc(2*ny);
-		ws = gsl_fft_complex_workspace_alloc(2*ny);
-		b = new double[4*ny];
-		for(i=0;i<nx;i++)	for(k=0;k<nz;k++)
-		{
-			memset(b,0,4*ny*sizeof(double));	b[0] = a[i+nx*ny*k];	b[2*ny] = -a[i+nx*ny*k];
-			for(j=1;j<ny;j++)	{	b[2*j] = a[i+nx*(ny*k+j)];	b[4*ny-2*j] = -a[i+nx*(ny*k+j)];	}
-			gsl_fft_complex_transform(b, 1, 2*ny, wt, ws, forward);
-			for(j=0;j<ny;j++)	a[i+nx*(ny*k+j)] = -b[2*j+1]/sqrt(2.*ny);
-		}
-	}
-	if(strchr(dir,'z') && nz>1)
-	{
-		wt = gsl_fft_complex_wavetable_alloc(2*nz);
-		ws = gsl_fft_complex_workspace_alloc(2*nz);
-		b = new double[4*nz];	k = nx*ny;
-		for(i=0;i<k;i++)
-		{
-			memset(b,0,4*nz*sizeof(double));	b[0] = a[i];	b[2*nx] = -a[i];
-			for(j=1;j<nx;j++)	{	b[2*j] = a[i+k*j];	b[4*nx-2*j] = -a[i+k*j];	}
-			gsl_fft_complex_transform(b, 1, 2*nz, wt, ws, forward);
-			for(j=0;j<nz;j++)	a[i+k*j] = -b[2*j+1]/sqrt(2.*nz);
-		}
-	}
-	if(b)
-	{	delete []b;
-		gsl_fft_complex_workspace_free(ws);
-		gsl_fft_complex_wavetable_free(wt);	}
-#endif
-}
-void MGL_EXPORT mgl_data_sinfft_(uintptr_t *d, const char *dir,int l)
-{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
-	mgl_data_sinfft(_DT_,s);	delete []s;	}
-//-----------------------------------------------------------------------------
-HMDT MGL_EXPORT mgl_transform_a(HCDT am, HCDT ph, const char *tr)
-{
-	long nx = am->GetNx(), ny = am->GetNy(), nz = am->GetNz();
-	if(nx*ny*nz != ph->GetNx()*ph->GetNy()*ph->GetNz() || !tr || tr[0]==0)
-		return (new mglData);
-	mglData re(nx,ny,nz), im(nx,ny,nz);
-	const mglData *da=dynamic_cast<const mglData *>(am);
-	const mglData *dp=dynamic_cast<const mglData *>(ph);
-	if(da && dp)	for(long i=0;i<nx*ny*nz;i++)
-	{	re.a[i] = da->a[i]*cos(dp->a[i]);
-		im.a[i] = da->a[i]*sin(dp->a[i]);	}
-	else	for(long i=0;i<nx*ny*nz;i++)
-	{	re.a[i] = am->vthr(i)*cos(ph->vthr(i));
-		im.a[i] = am->vthr(i)*sin(ph->vthr(i));	}
-	return mgl_transform(&re, &im, tr);
-}
-//-----------------------------------------------------------------------------
-HMDT MGL_EXPORT mgl_transform(HCDT re, HCDT im, const char *tr)
-{
-	if(!tr || *tr==0)	return 0;
-	long nx = re->GetNx(), ny = re->GetNy(), nz = re->GetNz();
-	if(nx*ny*nz != im->GetNx()*im->GetNy()*im->GetNz() || !tr || tr[0]==0)
-		return (new mglData);
-	mglData rr(re),ii(im);
-	if(strchr(tr,'i') && strchr(tr,'f'))	// general case
-	{
-		if(tr[0]=='f')	mgl_data_fourier(&rr,&ii,"x");
-		if(tr[0]=='i')	mgl_data_fourier(&rr,&ii,"xi");
-		if(tr[1]=='f')	mgl_data_fourier(&rr,&ii,"y");
-		if(tr[1]=='i')	mgl_data_fourier(&rr,&ii,"yi");
-		if(tr[2]=='f')	mgl_data_fourier(&rr,&ii,"z");
-		if(tr[2]=='i')	mgl_data_fourier(&rr,&ii,"zi");
-	}
-	else if(strchr(tr,'f'))	// do Fourier only once for speeding up
-	{
-		char str[4] = "   ";
-		if(tr[0]=='f')	str[0]='x';
-		if(tr[1]=='f')	str[1]='y';
-		if(tr[2]=='f')	str[2]='z';
-		mgl_data_fourier(&rr,&ii,str);
-	}
-	else if(strchr(tr,'i'))	// do Fourier only once for speeding up
-	{
-		char str[5] = "   i";
-		if(tr[0]=='i')	str[0]='x';
-		if(tr[1]=='i')	str[1]='y';
-		if(tr[2]=='i')	str[2]='z';
-		mgl_data_fourier(&rr,&ii,str);
-	}
-	else if(strchr(tr,'s'))	// do Fourier only once for speeding up
-	{
-		if(tr[0]=='s')	{	rr.SinFFT("x");	ii.SinFFT("x");	}
-		if(tr[1]=='s')	{	rr.SinFFT("y");	ii.SinFFT("y");	}
-		if(tr[2]=='s')	{	rr.SinFFT("z");	ii.SinFFT("z");	}
-	}
-	else if(strchr(tr,'c'))	// do Fourier only once for speeding up
-	{
-		if(tr[0]=='c')	{	rr.CosFFT("x");	ii.CosFFT("x");	}
-		if(tr[1]=='c')	{	rr.CosFFT("y");	ii.CosFFT("y");	}
-		if(tr[2]=='c')	{	rr.CosFFT("z");	ii.CosFFT("z");	}
-	}
-	else if(strchr(tr,'h'))	// do Fourier only once for speeding up
-	{
-		if(tr[0]=='h')	{	rr.Hankel("x");	ii.Hankel("x");	}
-		if(tr[1]=='h')	{	rr.Hankel("y");	ii.Hankel("y");	}
-		if(tr[2]=='h')	{	rr.Hankel("z");	ii.Hankel("z");	}
-	}
-	mglData *d = new mglData(nx, ny, nz);
-	register long i;
-	for(i=0;i<nx*ny*nz;i++)	d->a[i] = hypot(rr.a[i],ii.a[i]);
-	return d;
-}
-//-----------------------------------------------------------------------------
-uintptr_t MGL_EXPORT mgl_transform_a_(uintptr_t *am, uintptr_t *ph, const char *tr, int l)
-{	char *s=new char[l+1];	memcpy(s,tr,l);	s[l]=0;
-	uintptr_t res = uintptr_t(mgl_transform_a(_DA_(am),_DA_(ph),s));
-	delete []s;		return res;	}
-uintptr_t MGL_EXPORT mgl_transform_(uintptr_t *re, uintptr_t *im, const char *tr, int l)
-{	char *s=new char[l+1];	memcpy(s,tr,l);	s[l]=0;
-	uintptr_t res = uintptr_t(mgl_transform(_DA_(re),_DA_(im),s));
-	delete []s;		return res;	}
-//-----------------------------------------------------------------------------
 MGL_NO_EXPORT void *mgl_eqmul(void *par)
 {
 	mglThreadD *t=(mglThreadD *)par;
diff --git a/src/eval.cpp b/src/eval.cpp
index 8c2982f..8e180a6 100644
--- a/src/eval.cpp
+++ b/src/eval.cpp
@@ -182,28 +182,25 @@ mglFormula::mglFormula(const char *string)
 	if(!string)	{	Kod = EQ_NUM;	Res = 0;	return;	}
 	char *str = new char[strlen(string)+1];
 	strcpy(str,string);
-	static char Buf[2048];
 	long n,len;
 	mgl_strtrim(str);
 	mgl_strlwr(str);
 	len=strlen(str);
 	if(str[0]==0) {	delete []str;	return;	}
-	if(str[0]=='(' && mglCheck(&(str[1]),len-2))	// remove braces
+	if(str[0]=='(' && mglCheck(str+1,len-2))	// remove braces
 	{
-		strcpy(Buf,str+1);
-		len-=2;	Buf[len]=0;
-		strcpy(str,Buf);
+		memmove(str,str+1,len);
+		len-=2;	str[len]=0;
 	}
 	len=strlen(str);
 	n=mglFindInText(str,"&|");				// lowest priority -- logical
 	if(n>=0)
 	{
 		if(str[n]=='|') Kod=EQ_OR;	else Kod=EQ_AND;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormula(Buf);
-		Right=new mglFormula(Buf+n+1);
-		delete []str;
-		return;
+		str[n]=0;
+		Left=new mglFormula(str);
+		Right=new mglFormula(str+n+1);
+		delete []str;	return;
 	}
 	n=mglFindInText(str,"<>=");				// low priority -- conditions
 	if(n>=0)
@@ -211,41 +208,36 @@ mglFormula::mglFormula(const char *string)
 		if(str[n]=='<') Kod=EQ_LT;
 		else if(str[n]=='>') Kod=EQ_GT;
 		else Kod=EQ_EQ;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormula(Buf);
-		Right=new mglFormula(Buf+n+1);
-		delete []str;
-		return;
+		str[n]=0;
+		Left=new mglFormula(str);
+		Right=new mglFormula(str+n+1);
+		delete []str;	return;
 	}
 	n=mglFindInText(str,"+-");				// normal priority -- additions
 	if(n>=0 && (n<2 || str[n-1]!='e' || (str[n-2]!='.' && !isdigit(str[n-2]))))
 	{
 		if(str[n]=='+') Kod=EQ_ADD; else Kod=EQ_SUB;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormula(Buf);
-		Right=new mglFormula(Buf+n+1);
-		delete []str;
-		return;
+		str[n]=0;
+		Left=new mglFormula(str);
+		Right=new mglFormula(str+n+1);
+		delete []str;	return;
 	}
 	n=mglFindInText(str,"*/");				// high priority -- multiplications
 	if(n>=0)
 	{
 		if(str[n]=='*') Kod=EQ_MUL; else Kod=EQ_DIV;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormula(Buf);
-		Right=new mglFormula(Buf+n+1);
-		delete []str;
-		return;
+		str[n]=0;
+		Left=new mglFormula(str);
+		Right=new mglFormula(str+n+1);
+		delete []str;	return;
 	}
 	n=mglFindInText(str,"^");				// highest priority -- power
 	if(n>=0)
 	{
-		Kod=EQ_IPOW;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormula(Buf);
-		Right=new mglFormula(Buf+n+1);
-		delete []str;
-		return;
+		Kod=EQ_IPOW;		str[n]=0;
+		Left=new mglFormula(str);
+		Right=new mglFormula(str+n+1);
+		delete []str;	return;
 	}
 
 	for(n=0;n<len;n++)	if(str[n]=='(')	break;
@@ -262,13 +254,9 @@ mglFormula::mglFormula(const char *string)
 	else
 	{
 		char name[128];
-		strcpy(name,str);
-//		strcpy(Buf,str);
-		name[n]=0;
-//		len-=n;
-		memcpy(Buf,&(str[n+1]),len-n);
-		len=strlen(Buf);
-		Buf[--len]=0;
+		strncpy(name,str,128);	name[127]=name[n]=0;
+		memmove(str,str+n+1,len-n);
+		len=strlen(str);		str[--len]=0;
 		if(!strncmp(name,"jacobi_",7))
 			memmove(name,name+7,(strlen(name+7)+1)*sizeof(char));
 		if(name[0]=='a')
@@ -379,14 +367,14 @@ mglFormula::mglFormula(const char *string)
 		else if(!strcmp(name,"zeta"))	Kod=EQ_ZETA;
 		else if(!strcmp(name,"z"))		Kod=EQ_Z;
 		else {	delete []str;	return;	}	// unknown function
-		n=mglFindInText(Buf,",");
+		n=mglFindInText(str,",");
 		if(n>=0)
 		{
-			Buf[n]=0;
-			Left=new mglFormula(Buf);
-			Right=new mglFormula(&(Buf[n+1]));
+			str[n]=0;
+			Left=new mglFormula(str);
+			Right=new mglFormula(str+n+1);
 		}
-		else	Left=new mglFormula(Buf);
+		else	Left=new mglFormula(str);
 	}
 	delete []str;
 }
@@ -465,7 +453,7 @@ double MGL_NO_EXPORT cgt(double a,double b)	{return a>b?1:0;}
 double MGL_NO_EXPORT add(double a,double b)	{return a+b;}
 double MGL_NO_EXPORT sub(double a,double b)	{return a-b;}
 double MGL_NO_EXPORT mul(double a,double b)	{return a&&b?a*b:0;}
-double MGL_NO_EXPORT div(double a,double b)	{return b?a/b:NAN;}
+double MGL_NO_EXPORT del(double a,double b)	{return b?a/b:NAN;}
 double MGL_NO_EXPORT ipw(double a,double b)	{return fabs(b-int(b))<1e-5 ? mgl_ipow(a,int(b)) : pow(a,b);}
 double MGL_NO_EXPORT llg(double a,double b)	{return log(a)/log(b);}
 #if MGL_HAVE_GSL
@@ -502,7 +490,7 @@ mreal mglFormula::CalcIn(const mreal *a1) const
 			,0,0,0,0,0,0,0,0
 #endif
 		};
-	func_2 f2[22] = {clt,cgt,ceq,cor,cand,add,sub,mul,div,ipw,pow,fmod,llg,arg
+	func_2 f2[22] = {clt,cgt,ceq,cor,cand,add,sub,mul,del,ipw,pow,fmod,llg,arg
 #if MGL_HAVE_GSL
 			,gsl_sf_bessel_Jnu,gsl_sf_bessel_Ynu,
 			gsl_sf_bessel_Inu,gsl_sf_bessel_Knu,
diff --git a/src/evalc.cpp b/src/evalc.cpp
index 1265b96..eb945ba 100644
--- a/src/evalc.cpp
+++ b/src/evalc.cpp
@@ -79,7 +79,6 @@ mglFormulaC::mglFormulaC(const char *string)
 //printf("%s\n",string);	fflush(stdout);
 	char *str = new char[strlen(string)+1];
 	strcpy(str,string);
-	static char Buf[2048];
 	long n,len;
 	mgl_strtrim(str);
 	mgl_strlwr(str);
@@ -87,40 +86,35 @@ mglFormulaC::mglFormulaC(const char *string)
 	if(str[0]==0) {	delete []str;	return;	}
 	if(str[0]=='(' && mglCheck(&(str[1]),len-2))	// remove braces
 	{
-		strcpy(Buf,str+1);
-		len-=2;	Buf[len]=0;
-		strcpy(str,Buf);
+		memmove(str,str+1,len);
+		len-=2;	str[len]=0;
 	}
 	len=strlen(str);
 	n=mglFindInText(str,"+-");				// normal priority -- additions
-	if(n>=0)
+	if(n>=0 && (n<2 || str[n-1]!='e' || (str[n-2]!='.' && !isdigit(str[n-2]))))
 	{
 		if(str[n]=='+') Kod=EQ_ADD; else Kod=EQ_SUB;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormulaC(Buf);
-		Right=new mglFormulaC(Buf+n+1);
-		delete []str;
-		return;
+		str[n]=0;
+		Left=new mglFormulaC(str);
+		Right=new mglFormulaC(str+n+1);
+		delete []str;	return;
 	}
 	n=mglFindInText(str,"*/");				// high priority -- multiplications
 	if(n>=0)
 	{
 		if(str[n]=='*') Kod=EQ_MUL; else Kod=EQ_DIV;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormulaC(Buf);
-		Right=new mglFormulaC(Buf+n+1);
-		delete []str;
-		return;
+		str[n]=0;
+		Left=new mglFormulaC(str);
+		Right=new mglFormulaC(str+n+1);
+		delete []str;	return;
 	}
 	n=mglFindInText(str,"^");				// highest priority -- power
 	if(n>=0)
 	{
-		Kod=EQ_IPOW;
-		strcpy(Buf,str); Buf[n]=0;
-		Left=new mglFormulaC(Buf);
-		Right=new mglFormulaC(Buf+n+1);
-		delete []str;
-		return;
+		Kod=EQ_IPOW;		str[n]=0;
+		Left=new mglFormulaC(str);
+		Right=new mglFormulaC(str+n+1);
+		delete []str;	return;
 	}
 
 	for(n=0;n<len;n++)	if(str[n]=='(')	break;
@@ -138,13 +132,9 @@ mglFormulaC::mglFormulaC(const char *string)
 	else
 	{
 		char name[128];
-		strcpy(name,str);
-//		strcpy(Buf,str);
-		name[n]=0;
-//		len-=n;
-		memcpy(Buf,&(str[n+1]),len-n);
-		len=strlen(Buf);
-		Buf[--len]=0;
+		strncpy(name,str,128);	name[127]=name[n]=0;
+		memmove(str,str+n+1,len-n);
+		len=strlen(str);		str[--len]=0;
 		if(!strcmp(name,"sin")) Kod=EQ_SIN;
 		else if(!strcmp(name,"cos")) Kod=EQ_COS;
 		else if(!strcmp(name,"tg")) Kod=EQ_TAN;
@@ -166,15 +156,15 @@ mglFormulaC::mglFormulaC(const char *string)
 		else if(!strcmp(name,"ln")) Kod=EQ_LN;
 		else if(!strcmp(name,"abs")) Kod=EQ_ABS;
 		else {	delete []str;	return;	}	// unknown function
-		n=mglFindInText(Buf,",");
+		n=mglFindInText(str,",");
 		if(n>=0)
 		{
-			Buf[n]=0;
-			Left=new mglFormulaC(Buf);
-			Right=new mglFormulaC(&(Buf[n+1]));
+			str[n]=0;
+			Left=new mglFormulaC(str);
+			Right=new mglFormulaC(str+n+1);
 		}
 		else
-			Left=new mglFormulaC(Buf);
+			Left=new mglFormulaC(str);
 	}
 	delete []str;
 }
diff --git a/src/evalp.cpp b/src/evalp.cpp
index 31d4a97..a0d7700 100644
--- a/src/evalp.cpp
+++ b/src/evalp.cpp
@@ -85,7 +85,7 @@ double MGL_NO_EXPORT cgt(double a,double b);//	{return a>b?1:0;}
 double MGL_NO_EXPORT add(double a,double b);//	{return a+b;}
 double MGL_NO_EXPORT sub(double a,double b);//	{return a-b;}
 double MGL_NO_EXPORT mul(double a,double b);//	{return a&&b?a*b:0;}
-double MGL_NO_EXPORT div(double a,double b);//	{return b?a/b:NAN;}
+double MGL_NO_EXPORT del(double a,double b);//	{return b?a/b:NAN;}
 double MGL_NO_EXPORT ipw(double a,double b);//	{return mgl_ipow(a,int(b));}
 double MGL_NO_EXPORT llg(double a,double b);//	{return log(a)/log(b);}
 //double MGL_NO_EXPORT asinh(double x);//	{	return log(x+sqrt(x*x+1));	}
@@ -116,17 +116,15 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 #endif
 	mglData res;
 	if(!string || !(*string) || mglFormulaError)	return res;	// nothing to parse
-	wchar_t *str = new wchar_t[wcslen(string)+1];
+	wchar_t *str = new wchar_t[wcslen(string)+1],ch;
 	wcscpy(str,string);
-	static wchar_t Buf[2048];
 	long n,len;
 	mgl_wcstrim(str);	//	mgl_wcslwr(str);
 	len=wcslen(str);
 	if(str[0]=='(' && mglCheck(&(str[1]),len-2))	// remove braces
 	{
-		wcscpy(Buf,str+1);
-		len-=2;	Buf[len]=0;
-		wcscpy(str,Buf);
+		memmove(str,str+1,len*sizeof(wchar_t));
+		len-=2;	str[len]=0;
 	}
 	len=wcslen(str);
 	if(str[0]=='[')	// this is manual subdata
@@ -140,8 +138,7 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 			if(str[i]==']' && br>0)	br--;
 			if(str[i]==',' && !br)
 			{
-				wcscpy(Buf,str+j);	Buf[i-j]=0;
-				a1=mglFormulaCalc(Buf, arg);
+				str[i]=0;	a1=mglFormulaCalc(str+j, arg);
 				if(j==1)
 				{	res = a1;	ar = (a1.nx==1);	mt = (a1.nx>1 && a1.ny==1);	}
 				else
@@ -156,8 +153,7 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 				j=i+1;
 			}
 		}
-		wcscpy(Buf,str+j);	Buf[i-j]=0;
-		a1=mglFormulaCalc(Buf, arg);
+		str[i]=0;	a1=mglFormulaCalc(str+j, arg);
 		if(j==1)
 		{	res = a1;	ar = (a1.nx==1);	mt = (a1.nx>1 && a1.ny==1);	}
 		else
@@ -175,54 +171,53 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 	n=mglFindInText(str,"&|");				// lowest priority -- logical
 	if(n>=0)
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		res = mglApplyOper(Buf,Buf+n+1,arg, str[n]=='|'?cor:cand);
+		ch=str[n];	str[n]=0;
+		res = mglApplyOper(str,str+n+1,arg, ch=='|'?cor:cand);
 		delete []str;		return res;
 	}
 	n=mglFindInText(str,"<>=");				// low priority -- conditions
 	if(n>=0)
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		if(str[n]=='<')			res = mglApplyOper(Buf,Buf+n+1,arg, clt);
-		else if(str[n]=='>')	res = mglApplyOper(Buf,Buf+n+1,arg, cgt);
-		else	res = mglApplyOper(Buf,Buf+n+1,arg, ceq);
+		ch=str[n];	str[n]=0;
+		if(ch=='<')		res = mglApplyOper(str,str+n+1,arg, clt);
+		else if(ch=='>')	res = mglApplyOper(str,str+n+1,arg, cgt);
+		else 	res = mglApplyOper(str,str+n+1,arg, ceq);
 		delete []str;		return res;
 	}
 	n=mglFindInText(str,"+-");				// normal priority -- additions
 	if(n>=0 && (n<2 || str[n-1]!='e' || (str[n-2]!='.' && !isdigit(str[n-2]))))
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		res = mglApplyOper(Buf,Buf+n+1,arg, str[n]=='+'?add:sub);
+		ch=str[n];	str[n]=0;
+		res = mglApplyOper(str,str+n+1,arg, ch=='+'?add:sub);
 		delete []str;		return res;
 	}
 	n=mglFindInText(str,"*/");				// high priority -- multiplications
 	if(n>=0)
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		if(str[n]=='*')	res = mglApplyOper(Buf,Buf+n+1,arg, mul);
-		else			res = mglApplyOper(Buf,Buf+n+1,arg, div);
+		ch=str[n];	str[n]=0;
+		res = mglApplyOper(str,str+n+1,arg, ch=='*'?mul:del);
 		delete []str;		return res;
 	}
 	n=mglFindInText(str,"@");				// high priority -- combine
 	if(n>=0)
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		const mglData &a = mglFormulaCalc(Buf,arg), &b = mglFormulaCalc(Buf+n+1,arg);
+		str[n]=0;
+		const mglData &a = mglFormulaCalc(str,arg), &b = mglFormulaCalc(str+n+1,arg);
 		delete []str;		return a.Combine(b);
 	}
 	n=mglFindInText(str,"^");				// highest priority -- power
 	if(n>=0)
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		res = mglApplyOper(Buf,Buf+n+1,arg, ipw);
+		str[n]=0;
+		res = mglApplyOper(str,str+n+1,arg, ipw);
 		delete []str;		return res;
 	}
 	n=mglFindInText(str,":");				// highest priority -- array
 	if(n>=0 && wcscmp(str,L":"))
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
-		mglData a1=mglFormulaCalc(Buf, arg);
-		mglData a2=mglFormulaCalc(Buf+n+1, arg);
+		str[n]=0;
+		mglData a1=mglFormulaCalc(str, arg);
+		mglData a2=mglFormulaCalc(str+n+1, arg);
 		res.Create(abs(int(a2.a[0]+0.5)-int(a1.a[0]+0.5))+1);
 		res.Fill(a1.a[0], a2.a[0]);
 		delete []str;		return res;
@@ -230,10 +225,10 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 	n=mglFindInText(str,".");				// highest priority -- suffixes
 	if(n>=0)
 	{
-		wcscpy(Buf,str);	Buf[n]=0;
+		str[n]=0;
 		mreal x,y,z,k,v=NAN;
-		mglData d = mglFormulaCalc(Buf, arg);
-		const wchar_t *p=Buf+n+1;
+		mglData d = mglFormulaCalc(str, arg);
+		const wchar_t *p=str+n+1;
 		if(!wcscmp(p,L"a"))			v = d.a[0];
 		else if(!wcscmp(p,L"fst"))	{	long i=-1,j=-1,l=-1;	v = d.Find(0,i,j,l);	}
 		else if(!wcscmp(p,L"lst"))	{	long i=-1,j=-1,l=-1;	v = d.Last(0,i,j,l);	}
@@ -264,6 +259,7 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 		else if(!wcscmp(p,L"ka"))	{	d.Momentum('a',x,y,z,k);v=k;	}
 		// if this is valid suffix when finish parsing (it can be mreal number)
 		if(!mgl_isnan(v))	{	res.a[0] = v;	delete []str;	return res;	}
+		else 	str[n]='.';
 	}
 	for(n=0;n<len;n++)	if(str[n]=='(')	break;
 	if(n>=len)		// this is number or variable
@@ -285,9 +281,9 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 	{
 		register long i;
 		wchar_t name[128];
-		wcscpy(name,str);	name[n]=0;
-		wcscpy(Buf,str+n+1);
-		len=wcslen(Buf);	Buf[--len]=0;
+		wcsncpy(name,str,128);	name[127]=name[n]=0;
+		memmove(str,str+n+1,(len-n)*sizeof(wchar_t));
+		len=wcslen(str);		str[--len]=0;
 		mglVar *v = arg->FindVar(name);
 		if(!v)
 		{
@@ -296,10 +292,10 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 		}
 		if(v)	// subdata
 		{
-			if(Buf[0]=='\'' && Buf[len-1]=='\'')	// this is column call
+			if(str[0]=='\'' && str[len-1]=='\'')	// this is column call
 			{
 				char *buf = new char[len];
-				Buf[len-1]=0;	mgl_wcstombs(buf, Buf+1, len-1);
+				str[len-1]=0;	mgl_wcstombs(buf, str+1, len-1);
 				res=v->Column(buf);	delete []buf;
 			}
 			else
@@ -307,67 +303,67 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 				long m;
 				mglData a1, a2, a3;
 				a1.a[0] = a2.a[0] = a3.a[0] = -1;
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n>0)
 				{
-					Buf[n]=0;	m=mglFindInText(Buf,",");
+					str[n]=0;	m=mglFindInText(str,",");
 					if(m>0)
 					{
-						Buf[m]=0;
-						a1 = mglFormulaCalc(Buf, arg);
-						a2 = mglFormulaCalc(Buf+m+1, arg);
-						a3 = mglFormulaCalc(Buf+n+1, arg);
+						str[m]=0;
+						a1 = mglFormulaCalc(str, arg);
+						a2 = mglFormulaCalc(str+m+1, arg);
+						a3 = mglFormulaCalc(str+n+1, arg);
 					}
 					else
 					{
-						a1 = mglFormulaCalc(Buf, arg);
-						a2 = mglFormulaCalc(Buf+n+1, arg);
+						a1 = mglFormulaCalc(str, arg);
+						a2 = mglFormulaCalc(str+n+1, arg);
 					}
 				}
-				else	a1 = mglFormulaCalc(Buf, arg);
+				else	a1 = mglFormulaCalc(str, arg);
 				res = v->SubData(a1,a2,a3);
 			}
 		}
 		else if(name[0]=='a')	// function
 		{
 			if(!wcscmp(name+1,L"sin"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = asin(res.a[i]);	}
 			else if(!wcscmp(name+1,L"cos"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = acos(res.a[i]);	}
 			else if(!wcscmp(name+1,L"tan"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = atan(res.a[i]);	}
 			else if(!wcscmp(name+1,L"rg"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf+n+1,Buf,arg, atan2);	}
+				{	str[n]=0;	res = mglApplyOper(str+n+1,str,arg, atan2);	}
 			}
 			else if(!wcscmp(name+1,L"bs"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = fabs(res.a[i]);	}
 #if MGL_HAVE_GSL
 			else if(!wcscmp(name+1,L"i"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_airy_Ai(res.a[i],GSL_PREC_SINGLE);	}
 			else if(!wcscmp(name+1,L"iry_ai"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_airy_Ai(res.a[i],GSL_PREC_SINGLE);	}
 			else if(!wcscmp(name+1,L"iry_dai"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_airy_Ai_deriv(res.a[i],GSL_PREC_SINGLE);	}
 			else if(!wcscmp(name+1,L"iry_bi"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_airy_Bi(res.a[i],GSL_PREC_SINGLE);	}
 			else if(!wcscmp(name+1,L"iry_dbi"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_airy_Bi_deriv(res.a[i],GSL_PREC_SINGLE);	}
 		}
@@ -375,13 +371,13 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 		{
 			if(!wcscmp(name+1,L"eta"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_beta);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_beta);	}
 			}
 			else if(!wcscmp(name+1,L"i"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_airy_Bi(res.a[i],GSL_PREC_SINGLE);	}
 #endif
@@ -389,98 +385,98 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 		else if(name[0]=='c')
 		{
 			if(!wcscmp(name+1,L"os"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = cos(res.a[i]);	}
 			else if(!wcscmp(name+1,L"osh") || !wcscmp(name+1,L"h"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = cosh(res.a[i]);	}
 #if MGL_HAVE_GSL
 			else if(!wcscmp(name+1,L"i"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_Ci(res.a[i]);	}
 			else if(!wcscmp(name+1,L"essel_i"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Inu);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Inu);	}
 			}
 			else if(!wcscmp(name+1,L"essel_j"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Jnu);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Jnu);	}
 			}
 			else if(!wcscmp(name+1,L"essel_k"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Knu);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Knu);	}
 			}
 			else if(!wcscmp(name+1,L"essel_y"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Ynu);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Ynu);	}
 			}
 #endif
 		}
 		else if(name[0]=='e')
 		{
 			if(!wcscmp(name+1,L"xp"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = exp(res.a[i]);	}
 #if MGL_HAVE_GSL
 			else if(!wcscmp(name+1,L"rf"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_erf(res.a[i]);	}
 //			else if(!wcscmp(name+1,L"n"))	Kod=EQ_EN;	// NOTE: not supported
 			else if(!wcscmp(name+1,L"e") || !wcscmp(name+1,L"lliptic_ec"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_ellint_Ecomp(res.a[i],GSL_PREC_SINGLE);	}
 			else if(!wcscmp(name+1,L"k") || !wcscmp(name+1,L"lliptic_kc"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_ellint_Kcomp(res.a[i],GSL_PREC_SINGLE);	}
 			else if(name[0]==0 || !wcscmp(name+1,L"lliptic_e"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gslEllE);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gslEllE);	}
 			}
 			else if(!wcscmp(name+1,L"lliptic_f"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gslEllF);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gslEllF);	}
 			}
 
 			else if(!wcscmp(name+1,L"i"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_expint_Ei(res.a[i]);	}
 			else if(!wcscmp(name+1,L"1"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_expint_E1(res.a[i]);	}
 			else if(!wcscmp(name+1,L"2"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_expint_E2(res.a[i]);	}
 			else if(!wcscmp(name+1,L"ta"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_eta(res.a[i]);	}
 			else if(!wcscmp(name+1,L"i3"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_expint_3(res.a[i]);	}
 #endif
@@ -489,56 +485,56 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 		{
 			if(!wcscmp(name+1,L"og"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, llg);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, llg);	}
 			}
 			else if(!wcscmp(name+1,L"g"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = log10(res.a[i]);	}
 			else if(!wcscmp(name+1,L"n"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = log(res.a[i]);	}
 #if MGL_HAVE_GSL
 			else if(!wcscmp(name+1,L"i2"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_dilog(res.a[i]);	}
 			else if(!wcscmp(name+1,L"egendre"))
 			{
-				n=mglFindInText(Buf,",");
+				n=mglFindInText(str,",");
 				if(n<=0)	mglFormulaError=true;
 				else
-				{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gslLegP);	}
+				{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gslLegP);	}
 			}
 #endif
 		}
 		else if(name[0]=='s')
 		{
 			if(!wcscmp(name+1,L"qrt"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = sqrt(res.a[i]);	}
 			else if(!wcscmp(name+1,L"in"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = sin(res.a[i]);	}
 			else if(!wcscmp(name+1,L"tep"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = res.a[i]>0?1:0;	}
 			else if(!wcscmp(name+1,L"ign"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = res.a[i]>0?1:(res.a[i]<0?-1:0);	}
 			else if(!wcscmp(name+1,L"inh") || !wcscmp(name+1,L"h"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = sinh(res.a[i]);	}
 #if MGL_HAVE_GSL
 			else if(!wcscmp(name+1,L"i"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_Si(res.a[i]);	}
 			else if(!wcscmp(name+1,L"inc"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)
 					res.a[i] = gsl_sf_sinc(res.a[i]);	}
 #endif
@@ -546,87 +542,87 @@ mglData MGL_NO_EXPORT mglFormulaCalc(const wchar_t *string, mglParser *arg)
 		else if(name[0]=='t')
 		{
 			if(!wcscmp(name+1,L"g") || !wcscmp(name+1,L"an"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = tan(res.a[i]);	}
 			else if(!wcscmp(name+1,L"anh") || !wcscmp(name+1,L"h"))
-			{	res=mglFormulaCalc(Buf, arg);
+			{	res=mglFormulaCalc(str, arg);
 				for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = tanh(res.a[i]);	}
 		}
 		else if(!wcscmp(name,L"pow"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, pow);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, pow);	}
 		}
 		else if(!wcscmp(name,L"mod"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, fmod);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, fmod);	}
 		}
 		else if(!wcscmp(name,L"int"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)	res.a[i] = floor(res.a[i]);	}
 #if MGL_HAVE_GSL
 		else if(!wcscmp(name,L"i"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Inu);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Inu);	}
 		}
 		else if(!wcscmp(name,L"j"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Jnu);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Jnu);	}
 		}
 		else if(!wcscmp(name,L"k"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Knu);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Knu);	}
 		}
 		else if(!wcscmp(name,L"y"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gsl_sf_bessel_Ynu);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gsl_sf_bessel_Ynu);	}
 		}
 		else if(!wcscmp(name,L"f"))
 		{
-			n=mglFindInText(Buf,",");
+			n=mglFindInText(str,",");
 			if(n<=0)	mglFormulaError=true;
 			else
-			{	Buf[n]=0;	res = mglApplyOper(Buf,Buf+n+1,arg, gslEllF);	}
+			{	str[n]=0;	res = mglApplyOper(str,str+n+1,arg, gslEllF);	}
 		}
 		else if(!wcscmp(name,L"gamma"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)
 				res.a[i] = gsl_sf_gamma(res.a[i]);	}
 		else if(!wcscmp(name,L"w0"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)
 				res.a[i] = gsl_sf_lambert_W0(res.a[i]);	}
 		else if(!wcscmp(name,L"w1"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)
 				res.a[i] = gsl_sf_lambert_Wm1(res.a[i]);	}
 		else if(!wcscmp(name,L"psi"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)
 				res.a[i] = gsl_sf_psi(res.a[i]);	}
 		else if(!wcscmp(name,L"zeta"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)
 				res.a[i] = gsl_sf_zeta(res.a[i]);	}
 		else if(!wcscmp(name,L"z"))
-		{	res=mglFormulaCalc(Buf, arg);
+		{	res=mglFormulaCalc(str, arg);
 			for(i=0;i<res.nx*res.ny*res.nz;i++)
 				res.a[i] = gsl_sf_dawson(res.a[i]);	}
 #endif
diff --git a/src/exec.cpp b/src/exec.cpp
index 9abaa37..c185d27 100644
--- a/src/exec.cpp
+++ b/src/exec.cpp
@@ -1818,7 +1818,7 @@ int MGL_NO_EXPORT mgls_info(mglGraph *gr, long , mglArg *a, const char *k, const
 	if(!strcmp(k,"d"))	gr->SetWarn(-1,a[0].d->PrintInfo());
 	else if(!strcmp(k,"s"))	gr->SetWarn(-1,a[0].s.c_str());
 	else if(!strcmp(k,"n"))
-	{	char buf[128];	sprintf(buf,"value = %g",a[0].v);	gr->SetWarn(-1,buf);	}
+	{	char buf[128];	snprintf(buf,128,"value = %g",a[0].v);	gr->SetWarn(-1,buf);	}
 	else res = 1;	return res;
 }
 //-----------------------------------------------------------------------------
@@ -2154,7 +2154,7 @@ int MGL_NO_EXPORT mgls_fgets(mglGraph *gr, long , mglArg *a, const char *k, cons
 		memset(buf,0,1024);
 		if(!fgets(buf,1024,fp))
 		{
-			char b[32];	sprintf(b,"%d",n);
+			char b[32];	snprintf(b,32,"%d",n);
 			gr->SetWarn(mglWarnOpen,(a[2].s+" - line "+b).c_str());
 			fclose(fp);	return res;
 		}
@@ -2174,7 +2174,7 @@ int MGL_NO_EXPORT mgls_fgets(mglGraph *gr, long , mglArg *a, const char *k, cons
 		memset(buf,0,1024);
 		if(!fgets(buf,1024,fp))
 		{
-			char b[32];	sprintf(b,"%d",n);
+			char b[32];	snprintf(b,32,"%d",n);
 			gr->SetWarn(mglWarnOpen,(a[3].s+" - line "+b).c_str());
 			fclose(fp);	return res;
 		}
@@ -2367,6 +2367,23 @@ int MGL_NO_EXPORT mgls_ticklen(mglGraph *gr, long , mglArg *a, const char *k, co
 	else res = 1;	return res;
 }
 //-----------------------------------------------------------------------------
+int MGL_NO_EXPORT mgls_tickshift(mglGraph *gr, long , mglArg *a, const char *k, const char *)
+{
+	int res=0;
+	if(!strcmp(k,"n"))	gr->SetTickShift(mglPoint(a[0].v));
+	else if(!strcmp(k,"nn"))	gr->SetTickShift(mglPoint(a[0].v, a[1].v));
+	else if(!strcmp(k,"nnn"))	gr->SetTickShift(mglPoint(a[0].v, a[1].v, a[2].v));
+	else if(!strcmp(k,"nnnn"))	gr->SetTickShift(mglPoint(a[0].v, a[1].v, a[2].v, a[3].v));
+	else res = 1;	return res;
+}
+//-----------------------------------------------------------------------------
+int MGL_NO_EXPORT mgls_origintick(mglGraph *gr, long , mglArg *a, const char *k, const char *)
+{
+	int res=0;
+	if(!strcmp(k,"n"))	gr->SetOriginTick(a[0].v);
+	else res = 1;	return res;
+}
+//-----------------------------------------------------------------------------
 int MGL_NO_EXPORT mgls_axisstl(mglGraph *gr, long , mglArg *a, const char *k, const char *)
 {
 	int res=0;
@@ -2612,6 +2629,7 @@ mglCommand mgls_base_cmd[] = {
 	{"normsl","Normalize data slice by slice","normsl Dat v1 v2 ['dir' keep sym] ", mgls_normsl ,16},
 	{"once","Start/close commands which should executed only once","once val", 0, 6},
 	{"origin","Set axis origin","origin x0 y0 [z0]", mgls_origin ,14},
+	{"origintick","Set tick labels drawing at origin","origintick val", mgls_origintick ,14},
 	{"palette","Set palette for 1D plots","palette 'colors'", mgls_palette ,2},
 	{"pde","Solve PDE","pde Res 'ham' IniRe IniIm [dz k0]", mgls_pde ,4},
 	{"perspective","Set perspective","perspective val", mgls_perspective ,2},
@@ -2674,6 +2692,7 @@ mglCommand mgls_base_cmd[] = {
 	{"text","Draw text at some position or along curve","text x y 'txt' ['fmt' size]|x y z 'txt' ['fmt' size]|x y dx dy 'txt' ['fmt' size]|x y z dx dy dz 'txt' ['fmt' size]|Ydat 'txt' ['font' sise]|Xdat Ydat 'txt' ['font' sise]", mgls_text ,15},
 	{"textmark","Draw TeX mark at point position","textmark Ydat Rdat 'text' ['fmt']|Xdat Ydat Rdat 'text' ['fmt']|Xdat Ydat Zdat Rdat 'text' ['fmt']", mgls_textmark ,7},
 	{"ticklen","Set tick length","ticklen val [stt]", mgls_ticklen ,14},
+	{"tickshift","Set additional tick and axis labels shift","tickshift dx [dy dz dc]", mgls_tickshift ,14},
 	{"ticktime","Set ticks in time format","ticktime 'dir' [dv 'tmpl']", mgls_ticktime ,14},
 	{"tile","Draw horizontal tiles","tile Zdat ['fmt']|Xdat Ydat Zdat ['fmt']", mgls_tile ,8},
 	{"tiles","Draw horizontal tiles with variable size","tiles Zdat Rdat ['fmt']|Xdat Ydat Zdat Rdat ['fmt']", mgls_tiles ,10},
diff --git a/src/export.cpp b/src/export.cpp
index e34305f..8f165d4 100644
--- a/src/export.cpp
+++ b/src/export.cpp
@@ -197,7 +197,7 @@ void MGL_NO_EXPORT mgl_printf(void *fp, bool gz, const char *str, ...)
 	char buf[512];
 	va_list lst;
 	va_start(lst,str);
-	vsprintf(buf,str,lst);
+	vsnprintf(buf,512,str,lst);
 	va_end(lst);
 	if(gz)	gzprintf((gzFile)fp, "%s", buf);
 	else	fprintf((FILE *)fp, "%s", buf);
@@ -534,7 +534,7 @@ void MGL_EXPORT mgl_write_frame(HMGL gr, const char *fname,const char *descr)
 {
 	char buf[64];
 	if(!fname || !fname[0])
-	{	sprintf(buf,"%s%04d.jpg",_Gr_->PlotId.c_str(),_Gr_->GetNumFrame());	fname = buf;	}
+	{	snprintf(buf,64,"%s%04d.jpg",_Gr_->PlotId.c_str(),_Gr_->GetNumFrame());	fname = buf;	}
 	int len=strlen(fname);
 	if(!strcmp(fname+len-4,".jpg"))	mgl_write_jpg(gr,fname,descr);
 	if(!strcmp(fname+len-5,".jpeg"))mgl_write_jpg(gr,fname,descr);
@@ -567,28 +567,28 @@ void MGL_EXPORT mgl_write_frame_(uintptr_t *gr, const char *fname,const char *de
 void MGL_EXPORT mgl_show_image(HMGL gr, const char *viewer, int keep)
 {
 	char fname[128], *cmd = new char [128];
-	sprintf(fname,"%s.png", tmpnam(NULL));
+	snprintf(fname,128,"%s.png", tmpnam(NULL));
 	mgl_write_png_solid(gr,fname,"MathGL ShowImage file");
 	if(!viewer || !viewer[0])
 		viewer = MGL_DEF_VIEWER;
 #ifdef WIN32
 		if(keep)
 		{
-			sprintf(cmd,"%s %s &", viewer,fname);
+			snprintf(cmd,128,"%s %s &", viewer,fname);
 			if(system(cmd)==-1)	printf("Error to call external viewer\n");
 			//		sleep(2);
-			sprintf(cmd,"del %s", fname);
+			snprintf(cmd,128,"del %s", fname);
 		}
-		else	sprintf(cmd,"%s %s; del %s", viewer,fname,fname);
+		else	snprintf(cmd,128,"%s %s; del %s", viewer,fname,fname);
 #else
 		if(keep)
 		{
-			sprintf(cmd,"%s %s &", viewer,fname);
+			snprintf(cmd,128,"%s %s &", viewer,fname);
 			if(system(cmd)==-1)	printf("Error to call external viewer\n");
 			sleep(2);
-			sprintf(cmd,"rm %s", fname);
+			snprintf(cmd,128,"rm %s", fname);
 		}
-		else	sprintf(cmd,"%s %s; rm %s", viewer,fname,fname);
+		else	snprintf(cmd,128,"%s %s; rm %s", viewer,fname,fname);
 #endif
 		if(system(cmd)==-1)	printf("Error to call external viewer\n");
 		delete []cmd;
diff --git a/src/export_2d.cpp b/src/export_2d.cpp
index 4987e46..8bc307d 100644
--- a/src/export_2d.cpp
+++ b/src/export_2d.cpp
@@ -43,11 +43,11 @@ MGL_NO_EXPORT const char *mgl_get_dash(unsigned short d, mreal w)
 		if(((d>>j)&1) == p)	f++;
 		else
 		{
-			sprintf(b," %g",f*w);	s += b;
+			snprintf(b,32," %g",f*w);	s += b;
 			p = (d>>j)&1;	f = 1;	n++;
 		}
 	}
-	sprintf(b," %g",f*w);	s += b;
+	snprintf(b,32," %g",f*w);	s += b;
 	s += n%2 ? "" : " 0";
 	return s.c_str();
 }
@@ -286,12 +286,12 @@ void MGL_EXPORT mgl_write_eps(HMGL gr, const char *fname,const char *descr)
 		if(q.type<0)	continue;	// q.n1>=0 always
 		cp = _Gr_->GetColor(q);
 		const mglPnt p1 = gr->GetPnt(q.n1);
-		if(q.type>1)	sprintf(str,"%.2g %.2g %.2g rgb ", cp.r,cp.g,cp.b);
+		if(q.type>1)	snprintf(str,256,"%.2g %.2g %.2g rgb ", cp.r,cp.g,cp.b);
 
 		if(q.type==0)	// mark
 		{
 			mreal x0 = p1.x,y0 = p1.y;
-			sprintf(str,"%.2g lw %.2g %.2g %.2g rgb ", 50*q.s*q.w>1?50*q.s*q.w:1, cp.r,cp.g,cp.b);
+			snprintf(str,256,"%.2g lw %.2g %.2g %.2g rgb ", 50*q.s*q.w>1?50*q.s*q.w:1, cp.r,cp.g,cp.b);
 			wp=1;	// NOTE: this may renew line style if a mark inside!
 			if(q.s!=qs_old)
 			{
@@ -342,7 +342,7 @@ void MGL_EXPORT mgl_write_eps(HMGL gr, const char *fname,const char *descr)
 		}
 		else if(q.type==1)	// line
 		{
-			sprintf(str,"%.2g lw %.2g %.2g %.2g rgb ", q.w>1 ? q.w:1., cp.r,cp.g,cp.b);
+			snprintf(str,256,"%.2g lw %.2g %.2g %.2g rgb ", q.w>1 ? q.w:1., cp.r,cp.g,cp.b);
 			wp = q.w>1  ? q.w:1;	st = q.n3;
 			put_line(gr,fp,gz,i,wp,cp,st, "np %g %g mt ", "%g %g ll ", false, 1);
 			const char *sd = mgl_get_dash(q.n3,q.w);
@@ -662,7 +662,7 @@ void MGL_EXPORT mgl_write_tex(HMGL gr, const char *fname,const char *descr)
 		ii = (cp.r*255+25)/51;
 		jj = (cp.g*255+25)/51;
 		kk = (cp.b*255+25)/51;
-		sprintf(cname,"mgl_%d",ii+6*(jj+6*kk));
+		snprintf(cname,16,"mgl_%d",ii+6*(jj+6*kk));
 //		cname = mglColorName(cp);
 		const mglPnt p1=gr->GetPnt(q.n1);
 		mreal x=p1.x/100,y=p1.y/100,s=q.s/100;
diff --git a/src/export_3d.cpp b/src/export_3d.cpp
index 3eef724..976d563 100644
--- a/src/export_3d.cpp
+++ b/src/export_3d.cpp
@@ -507,19 +507,21 @@ void MGL_EXPORT mgl_write_off_(uintptr_t *gr, const char *fname,const char *desc
 bool mglCanvas::WriteJSON(const char *fname)
 {
 	bool fl = strcmp(fname,"-");
-	FILE *fp = fl ? fopen(fname, "wb") : stdout;
+	bool gz = fname[strlen(fname)-1]=='z';
+	void *fp = fl ? (gz ? (void*)gzopen(fname,"wt") : (void*)fopen(fname,"wt")) : stdout;
 	if (!fp)	return true;
 	ClearUnused();	// clear unused points
 	size_t i,l=Pnt.size();
-	fprintf(fp,"{\n\"width\":%d,\t\"height\":%d,\t\"depth\":%d,\t\"plotid\":\"%s\",\t\"npnts\":%lu,\t\"pnts\":[\n",
+	mgl_printf(fp, gz,"{\n\"width\":%d,\t\"height\":%d,\t\"depth\":%d,\t\"plotid\":\"%s\",\t\"npnts\":%lu,\t\"pnts\":[\n",
 			Width, Height, Depth, PlotId.c_str(), (unsigned long)l);
 	for(i=0;i<l;i++)
 	{
 		const mglPnt &q=Pnt[i];
-		fprintf(fp,"[%.4g,%.4g,%.4g]%c\n", q.xx, Height-q.yy, q.zz, i+1<l?',':' ');
+//		fprintf(fp,"[%.4g,%.4g,%.4g]%c\n", q.xx, Height-q.yy, q.zz, i+1<l?',':' ');
+		mgl_printf(fp, gz,"[%d,%d,%d]%c\n", int(q.xx), int(Height-q.yy), int(q.zz), i+1<l?',':' ');
 	}
 	l = Prm.size();
-	fprintf(fp,"],\t\"nprim\":%lu,\t\"prim\":[\n",(unsigned long)l);
+	mgl_printf(fp, gz,"],\t\"nprim\":%lu,\t\"prim\":[\n",(unsigned long)l);
 
 	std::vector<mglPoint> xy;	// vector for glyphs coordinates (to be separated from pnts)
 	for(i=0;i<l;i++)
@@ -538,41 +540,45 @@ bool mglCanvas::WriteJSON(const char *fname)
 		}
 		if(p.type==1 && n1>n2)	{	n1=p.n2;	n2=p.n1;	}
 		if(c.a==1 || p.type==0 || p.type==1 || p.type==4 || p.type==6)
-			fprintf(fp,"[%d,%ld,%ld,%ld,%ld,%d,%.4g,%.4g,%.4g,%.4g,\"#%02x%02x%02x\"]%c\n",
+//			fprintf(fp,"[%d,%ld,%ld,%ld,%ld,%d,%.4g,%.4g,%.4g,%.4g,\"#%02x%02x%02x\"]%c\n",
+			mgl_printf(fp, gz,"[%d,%ld,%ld,%ld,%ld,%d,%.3g,%.2g,%.2g,%.2g,\"#%02x%02x%02x\"]%c\n",
 				p.type, n1, n2, n3, n4, p.id, p.s, p.w==p.w?p.w:0, p.p==p.p?p.p:0,
 				0., int(255*c.r), int(255*c.g), int(255*c.b), i+1<l?',':' ');
 		else
-			fprintf(fp,"[%d,%ld,%ld,%ld,%ld,%d,%.4g,%.4g,%.4g,%.4g,\"rgba(%d,%d,%d,%.2g)\"]%c\n",
+//			fprintf(fp,"[%d,%ld,%ld,%ld,%ld,%d,%.4g,%.4g,%.4g,%.4g,\"rgba(%d,%d,%d,%.2g)\"]%c\n",
+			mgl_printf(fp, gz,"[%d,%ld,%ld,%ld,%ld,%d,%.3g,%.2g,%.2g,%.2g,\"rgba(%d,%d,%d,%.2g)\"]%c\n",
 				p.type, n1, n2, n3, n4, p.id, p.s, p.w==p.w?p.w:0, p.p==p.p?p.p:0,
 				0., int(255*c.r), int(255*c.g), int(255*c.b), c.a, i+1<l?',':' ');
 	}
 	
 	l = xy.size();
-	fprintf(fp,"],\t\"ncoor\":%lu,\t\"coor\":[\n",(unsigned long)l);
+	mgl_printf(fp, gz,"],\t\"ncoor\":%lu,\t\"coor\":[\n",(unsigned long)l);
 	for(i=0;i<l;i++)
 	{
 		const mglPoint &p=xy[i];
 		const mglPnt &q=Pnt[int(0.5+p.z)];
 		if(q.u==q.u)
-			fprintf(fp,"[%.4g,%.4g,%.4g,%.4g,%.4g]%c\n", p.x, p.y, q.u, q.v, q.w, i+1<l?',':' ');
+			mgl_printf(fp, gz,"[%.3g,%.3g,%.3g,%.3g,%.3g]%c\n", p.x, p.y, q.u, q.v, q.w, i+1<l?',':' ');
+//			fprintf(fp,"[%.4g,%.4g,%.4g,%.4g,%.4g]%c\n", p.x, p.y, q.u, q.v, q.w, i+1<l?',':' ');
 		else
-			fprintf(fp,"[%.4g,%.4g,1e11,1e11,1e11]%c\n", p.x, p.y, i+1<l?',':' ');
+//			fprintf(fp,"[%.4g,%.4g,1e11,1e11,1e11]%c\n", p.x, p.y, i+1<l?',':' ');
+			mgl_printf(fp, gz,"[%.2g,%.2g,1e11,1e11,1e11]%c\n", p.x, p.y, i+1<l?',':' ');
 	}
 
 	l = Glf.size();
-	fprintf(fp,"],\t\"nglfs\":%lu,\t\"glfs\":[\n",(unsigned long)l);
+	mgl_printf(fp, gz,"],\t\"nglfs\":%lu,\t\"glfs\":[\n",(unsigned long)l);
 	for(i=0;i<l;i++)
 	{
 		const mglGlyph &g=Glf[i];
-		fprintf(fp,"[%ld,%ld,\n\t[", g.nt, g.nl);
+		mgl_printf(fp, gz,"[%ld,%ld,\n\t[", g.nt, g.nl);
 		register long j;
-		for(j=0;j<6*g.nt;j++)	fprintf(fp,"%d%c", g.trig[j], j+1<6*g.nt?',':' ');
-		fprintf(fp,"],\n\t[");
-		for(j=0;j<2*g.nl;j++)	fprintf(fp,"%d%c", g.line[j], j+1<2*g.nl?',':' ');
-		fprintf(fp,"]\n]%c\n", i+1<l?',':' ');
+		for(j=0;j<6*g.nt;j++)	mgl_printf(fp, gz,"%d%c", g.trig[j], j+1<6*g.nt?',':' ');
+		mgl_printf(fp, gz,"],\n\t[");
+		for(j=0;j<2*g.nl;j++)	mgl_printf(fp, gz,"%d%c", g.line[j], j+1<2*g.nl?',':' ');
+		mgl_printf(fp, gz,"]\n]%c\n", i+1<l?',':' ');
 	}
-	fprintf(fp,"]\n}\n");
-	if(fl)	fclose(fp);
+	mgl_printf(fp, gz,"]\n}\n");
+	if(fl)	{	if(gz)	gzclose((gzFile)fp);	else	fclose((FILE *)fp);	}
 	return false;
 }
 //-----------------------------------------------------------------------------
@@ -1131,6 +1137,9 @@ void MGL_EXPORT mgl_write_x3d(HMGL gr, const char *fname,const char *descr)
 	}
 	delete []ng;
 
+	// resort in creation order for proper drawing of lines and faces
+	gr->resort();
+	
 	// primitive definition in groups
 	long npnt = gr->GetPntNum(), k;
 	long *pnt=new long[npnt];
@@ -1141,24 +1150,107 @@ void MGL_EXPORT mgl_write_x3d(HMGL gr, const char *fname,const char *descr)
 		std::vector<long> &p = gr->Grp[i].p;
 
 		// define coordinates, colors and so on
-		memset(pnt,-1,npnt*sizeof(long));
+		long line=-1, face=-1, other=-1;
 		for(j=0,k=0;j<p.size();j++)	// find points for this group
 		{
 			const mglPrim &q=gr->GetPrm(p[j]);
-			if(q.n1>=0 && pnt[q.n1]<0)	{	pnt[q.n1]=k;	k++;	}
-			if(q.type>0 && q.type<4 && q.n2>=0 && pnt[q.n2]<0)	{	pnt[q.n2]=k;	k++;	}
-			if(q.type>1 && q.type<4 && q.n3>=0 && pnt[q.n3]<0)	{	pnt[q.n3]=k;	k++;	}
-			if(q.type==3 && q.n4>=0 && pnt[q.n4]<0)	{	pnt[q.n4]=k;	k++;	}
+			if(q.type==1)	line=q.n1;	// find kind of primitives in the group
+			if(q.type==2 || q.type==3)	face =q.n1;
+			if(q.type>3 || q.type==0)	other=q.n1;
+		}
+
+		// now save lines
+		if(line>=0)
+		{
+			mglColor c=gr->GetPntC(line);
+			bool same=true;	// check if there are the same colors for all line segments
+			for(j=0;j<p.size();j++)
+			{
+				const mglPrim &q=gr->GetPrm(p[j]);
+				if(q.type==1 && c!=gr->GetPntC(q.n1))	same=false;
+			}
+			memset(pnt,-1,npnt*sizeof(long));
+			for(j=0,k=0;j<p.size();j++)	// rearrange points for this group
+			{
+				const mglPrim &q=gr->GetPrm(p[j]);
+				if(q.type!=1)	continue;
+				if(q.n1>=0 && pnt[q.n1]<0)	{	pnt[q.n1]=k;	k++;	}
+				if(q.n2>=0 && pnt[q.n2]<0)	{	pnt[q.n2]=k;	k++;	}
+			}
+			mgl_printf(fp, gz, "<Shape><Coordinate DEF='Lpnts_%ld' point='",i);
+			for(j=0;j<gr->GetPntNum();j++)	if(pnt[j]>=0)
+			{	const mglPnt &p=gr->GetPnt(j);	mgl_printf(fp, gz, "%g %g %g, ", p.x,p.y,p.z);	}
+			mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
+			mgl_printf(fp, gz, "<Color DEF='Lclrs_%ld' color='",i);
+			for(j=0;j<gr->GetPntNum();j++)	if(pnt[j]>=0)
+			{	const mglPnt &p=gr->GetPnt(j);	mgl_printf(fp, gz, "%g %g %g, ", p.r,p.g,p.b);	}
+			mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
+
+			// TODO save IndexedLineSet here + manual color is same==true
+			
+			mgl_printf(fp, gz, "</Shape>");
+		}
+
+		// now save faces
+		if(face>=0)
+		{
+			mglColor c=gr->GetPntC(face);
+			bool same=true;	// check if there are the same colors for all line segments
+			for(j=0;j<p.size();j++)
+			{
+				const mglPrim &q=gr->GetPrm(p[j]);
+				if((q.type==2 || q.type==3) && c!=gr->GetPntC(q.n1))	same=false;
+			}
+			memset(pnt,-1,npnt*sizeof(long));
+			for(j=0,k=0;j<p.size();j++)	// rearrange points for this group
+			{
+				const mglPrim &q=gr->GetPrm(p[j]);
+				if(q.type!=2 && q.type!=3)	continue;
+				if(q.n1>=0 && pnt[q.n1]<0)	{	pnt[q.n1]=k;	k++;	}
+				if(q.n2>=0 && pnt[q.n2]<0)	{	pnt[q.n2]=k;	k++;	}
+				if(q.n3>=0 && pnt[q.n3]<0)	{	pnt[q.n3]=k;	k++;	}
+				if(q.type==3 && q.n4>=0 && pnt[q.n4]<0)	{	pnt[q.n4]=k;	k++;	}
+			}
+			mgl_printf(fp, gz, "<Shape><Coordinate DEF='Fpnts_%ld' point='",i);
+			for(j=0;j<gr->GetPntNum();j++)	if(pnt[j]>=0)
+			{	const mglPnt &p=gr->GetPnt(j);	mgl_printf(fp, gz, "%g %g %g, ", p.x,p.y,p.z);	}
+			mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
+			mgl_printf(fp, gz, "<Color DEF='Fclrs_%ld' color='",i);
+			for(j=0;j<gr->GetPntNum();j++)	if(pnt[j]>=0)
+			{	const mglPnt &p=gr->GetPnt(j);	mgl_printf(fp, gz, "%g %g %g, ", p.r,p.g,p.b);	}
+			mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
+
+			// TODO save IndexedLineSet here + manual color is same==true
+
+			mgl_printf(fp, gz, "</Shape>");
+		}
+
+		// now save other primitives
+		if(other>=0)
+		{
+/*			memset(pnt,-1,npnt*sizeof(long));
+			for(j=0,k=0;j<p.size();j++)	// rearrange points for this group
+			{
+				const mglPrim &q=gr->GetPrm(p[j]);
+				if(q.type!=2 && q.type!=3)	continue;
+				if(q.n1>=0 && pnt[q.n1]<0)	{	pnt[q.n1]=k;	k++;	}
+				if(q.n2>=0 && pnt[q.n2]<0)	{	pnt[q.n2]=k;	k++;	}
+				if(q.n3>=0 && pnt[q.n3]<0)	{	pnt[q.n3]=k;	k++;	}
+				if(q.type==3 && q.n4>=0 && pnt[q.n4]<0)	{	pnt[q.n4]=k;	k++;	}
+			}
+			mgl_printf(fp, gz, "<Shape><Coordinate DEF='Fpnts_%ld' point='",i);
+			for(j=0;j<gr->GetPntNum();j++)	if(pnt[j]>=0)
+			{	const mglPnt &p=gr->GetPnt(j);	mgl_printf(fp, gz, "%g %g %g, ", p.x,p.y,p.z);	}
+			mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
+			mgl_printf(fp, gz, "<Color DEF='Fclrs_%ld' color='",i);
+			for(j=0;j<gr->GetPntNum();j++)	if(pnt[j]>=0)
+			{	const mglPnt &p=gr->GetPnt(j);	mgl_printf(fp, gz, "%g %g %g, ", p.r,p.g,p.b);	}
+			mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
+
+			// TODO save IndexedLineSet here + manual color is same==true
+
+			mgl_printf(fp, gz, "</Shape>");*/
 		}
-		
-		mgl_printf(fp, gz, "<Coordinate DEF='mypnts_%ld' point='",i);
-		for(i=0;i<gr->GetPntNum();i++)	if(pnt[i]>=0)
-		{	const mglPnt &p=gr->GetPnt(i);	mgl_printf(fp, gz, "%g %g %g, ", p.x,p.y,p.z);	}
-		mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
-		mgl_printf(fp, gz, "<Color DEF='myclrs' color='");
-		for(i=0;i<gr->GetPntNum();i++)	if(pnt[i]>=0)
-		{	const mglPnt &p=gr->GetPnt(i);	mgl_printf(fp, gz, "%g %g %g, ", p.r,p.g,p.b);	}
-		mgl_printf(fp, gz, "0.0 0.0 0.0'/>");
 		// no normals since mathgl ones are "signless" -- x3d should calculate it by itself
 
 		for(j=0;j<p.size();j++)
diff --git a/src/fft.cpp b/src/fft.cpp
new file mode 100644
index 0000000..33fd541
--- /dev/null
+++ b/src/fft.cpp
@@ -0,0 +1,813 @@
+/***************************************************************************
+ * fft.cpp is part of Math Graphic Library
+ * Copyright (C) 2007-2012 Alexey Balakin <mathgl.abalakin at gmail.ru>       *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Library General Public License as       *
+ *   published by the Free Software Foundation; either version 3 of the    *
+ *   License, or (at your option) any later version.                       *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU Library General Public     *
+ *   License along with this program; if not, write to the                 *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "mgl2/datac.h"
+#include "mgl2/data.h"
+#if MGL_HAVE_GSL
+#include <gsl/gsl_fft_complex.h>
+#include <gsl/gsl_dht.h>
+#include <gsl/gsl_sf.h>
+#endif
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mglStartThreadT(void *(*func)(void *), long n, void *a, double *b, const void *v, void **w, const long *p, const void *re, const void *im)
+{
+	if(!func)	return;
+#if MGL_HAVE_PTHREAD
+	if(mglNumThr<1)	mgl_set_num_thr(0);
+	if(mglNumThr>1)
+	{
+		pthread_t *tmp=new pthread_t[mglNumThr];
+		mglThreadT *par=new mglThreadT[mglNumThr];
+		register long i;
+		for(i=0;i<mglNumThr;i++)	// put parameters into the structure
+		{	par[i].n=n;	par[i].a=a;	par[i].v=v;	par[i].w=w;	par[i].b=b;
+			par[i].p=p;	par[i].re=re;	par[i].im=im;	par[i].id=i;	}
+		for(i=0;i<mglNumThr;i++)	pthread_create(tmp+i, 0, func, par+i);
+		for(i=0;i<mglNumThr;i++)	pthread_join(tmp[i], 0);
+		delete []tmp;	delete []par;
+	}
+	else
+#endif
+	{
+		mglNumThr = 1;
+		mglThreadT par;
+		par.n=n;	par.a=a;	par.b=b;	par.v=v;	par.w=w;
+		par.p=p;	par.re=re;	par.im=im;	par.id=0;
+		func(&par);
+	}
+}
+//-----------------------------------------------------------------------------
+MGL_EXPORT void *mgl_fft_alloc(long n, void **space, long nthr)
+{
+#if MGL_HAVE_GSL
+	for(long i=0;i<nthr;i++)	space[i] = gsl_fft_complex_workspace_alloc(n);
+	return gsl_fft_complex_wavetable_alloc(n);
+#else
+	register long i,j;
+	for(i=0;i<nthr;i++)	space[i] = new double[2*n];
+	double *c = new double[2*n*n];
+	for(i=0;i<n;i++)	for(j=0;j<n;j++)
+	{	c[2*(i+n*j)]=cos(2*M_PI*i*j/n);	c[2*(i+n*j)+1]=-sin(2*M_PI*i*j/n);	}
+	return c;
+#endif
+}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_fft_free(void *wt, void **ws, long nthr)
+{
+#if MGL_HAVE_GSL
+	for(long i=0;i<nthr;i++)	gsl_fft_complex_workspace_free((gsl_fft_complex_workspace*)(ws[i]));
+	gsl_fft_complex_wavetable_free((gsl_fft_complex_wavetable*)wt);
+#else
+	delete []((double*)wt);
+	for(long i=0;i<nthr;i++)	delete []((double*)(ws[i]));
+#endif
+}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_fft(double *x, long s, long n, const void *wt, void *ws, bool inv)
+{
+#if MGL_HAVE_GSL
+gsl_fft_complex_transform(x, s, n, (const gsl_fft_complex_wavetable*)wt, (gsl_fft_complex_workspace*)ws, inv?backward:forward);
+#else	// NOTE this is VERY slow!
+	const double *c = (const double *)wt;
+	double *d = (double *)ws, f = inv?1./n:1;
+	memset(d,0,2*n*sizeof(double));
+	register long i,j,ii,jj;
+	if(inv)	for(i=0;i<n;i++)	for(j=0;j<n;j++)
+	{
+		ii = 2*(i+n*j);	jj = 2*j*s;
+		d[2*i] 	+= x[jj]*c[ii]+x[jj+1]*c[ii+1];
+		d[2*i+1]+= x[jj+1]*c[ii]-x[jj]*c[ii+1];
+	}
+	else	for(i=0;i<n;i++)	for(j=0;j<n;j++)
+	{
+		ii = 2*(i+n*j);	jj = 2*j*s;
+		d[2*i] 	+= x[jj]*c[ii]-x[jj+1]*c[ii+1];
+		d[2*i+1]+= x[jj+1]*c[ii]+x[jj]*c[ii+1];
+	}
+	for(j=0;j<n;j++)
+	{	jj = 2*j*s;	x[jj] = d[2*j]*f;	x[jj+1] = d[2*j+1]*f;	}
+#endif
+}
+//-----------------------------------------------------------------------------
+MGL_NO_EXPORT void* mgl_fftx(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,nx=t->p[0];
+	for(i=t->id;i<t->n;i+=mglNumThr)
+		mgl_fft(t->b+2*nx*i, 1, nx, t->v, t->w[t->id], t->p[3]);
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_ffty(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,nx=t->p[0],ny=t->p[1];
+	for(i=t->id;i<t->n;i+=mglNumThr)
+		mgl_fft(t->b+2*(i%nx)+2*nx*ny*(i/nx), nx, ny, t->v, t->w[t->id], t->p[3]);
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_fftz(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,nx=t->p[0],ny=t->p[1],nz=t->p[2];
+	for(i=t->id;i<t->n;i+=mglNumThr)
+		mgl_fft(t->b+2*i, nx*ny, nz, t->v, t->w[t->id], t->p[3]);
+	return 0;
+}
+void MGL_EXPORT mgl_datac_fft(HADT d, const char *dir)
+{
+	if(!dir || *dir==0)	return;
+	long nx = d->nx, ny = d->ny, nz = d->nz;
+	if(mglNumThr<1)	mgl_set_num_thr(0);	// manually set number of threads
+	void *wt=0, *ws[mglNumThr];
+	long par[4]={nx,ny,nz,strchr(dir,'i')!=0}, i;
+#if MGL_USE_DOUBLE
+	double *a = (double *)(d->a);
+#else
+	double *a = new double[2*nx*ny*nz];	// manually convert to double
+	for(i=0;i<nx*ny*nz;i++)
+	{	a[2*i] = real(d->a[i]);	a[2*i+1] = imag(d->a[i]);	}
+#endif
+	if(strchr(dir,'x') && nx>1)
+	{
+		wt = mgl_fft_alloc(nx,ws,mglNumThr);
+		mglStartThreadT(mgl_fftx,ny*nz,0,a,wt,ws,par);
+	}
+	if(strchr(dir,'y') && ny>1)
+	{
+		wt = mgl_fft_alloc(ny,ws,mglNumThr);
+		mglStartThreadT(mgl_ffty,nx*nz,0,a,wt,ws,par);
+	}
+	if(strchr(dir,'z') && nz>1)
+	{
+		wt = mgl_fft_alloc(nz,ws,mglNumThr);
+		mglStartThreadT(mgl_fftz,nx*ny,0,a,wt,ws,par);
+	}
+	if(wt)	mgl_fft_free(wt,ws,mglNumThr);
+#if !MGL_USE_DOUBLE
+	for(i=0;i<nx*ny*nz;i++)	d->a[i] = dual(a[2*i], a[2*i+1]);
+	delete []a;
+#endif
+}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_data_fourier(HMDT re, HMDT im, const char *dir)
+{
+	if(!dir || *dir==0)	return;
+	long nx = re->nx, ny = re->ny, nz = re->nz;
+	if(nx*ny*nz != im->nx*im->ny*im->nz || !dir || dir[0]==0)	return;
+	if(mglNumThr<1)	mgl_set_num_thr(0);	// manually set number of threads
+	void *wt=0, *ws[mglNumThr];
+	long par[4]={nx,ny,nz,strchr(dir,'i')!=0}, i;
+	double *a = new double[2*nx*ny*nz];
+	for(i=0;i<nx*ny*nz;i++)
+	{	a[2*i] = re->a[i];	a[2*i+1] = im->a[i];	}
+	if(strchr(dir,'x') && nx>1)
+	{
+		wt = mgl_fft_alloc(nx,ws,mglNumThr);
+		mglStartThreadT(mgl_fftx,ny*nz,0,a,wt,ws,par);
+	}
+	if(strchr(dir,'y') && ny>1)
+	{
+		wt = mgl_fft_alloc(ny,ws,mglNumThr);
+		mglStartThreadT(mgl_ffty,nx*nz,0,a,wt,ws,par);
+	}
+	if(strchr(dir,'z') && nz>1)
+	{
+		wt = mgl_fft_alloc(nz,ws,mglNumThr);
+		mglStartThreadT(mgl_fftz,nx*ny,0,a,wt,ws,par);
+	}
+	if(wt)	mgl_fft_free(wt,ws,mglNumThr);
+	for(i=0;i<nx*ny*nz;i++)
+	{	re->a[i] = a[2*i];	im->a[i] = a[2*i+1];	}
+	delete []a;
+}
+//-----------------------------------------------------------------------------
+MGL_NO_EXPORT void* mgl_envx(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,nx=t->p[0],ny=t->p[1],nz=t->p[2];
+	double *b = t->b+2*nx*t->id;
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		for(j=0;j<nx;j++)	{	b[2*j] = a[j+i*nx];	b[2*j+1] = 0;	}
+		mgl_fft(b, 1, nx, t->v, t->w[t->id], false);
+		for(j=0;j<nx;j++)
+		{	b[j] /= nx/2.;	b[j+nx] = 0;	}
+		mgl_fft(b, 1, nx, t->v, t->w[t->id], true);
+		for(j=0;j<nx;j++)	a[j+i*nx] = hypot(b[2*j], b[2*j+1]);
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_envy(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,nx=t->p[0],ny=t->p[1],nz=t->p[2];
+	double *b = t->b+2*ny*t->id;
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		for(j=0;j<ny;j++)	{	b[2*j] = a[(i%nx)+nx*(j+ny*(i/nx))];	b[2*j+1] = 0;	}
+		mgl_fft(b, 1, ny, t->v, t->w[t->id], false);
+		for(j=0;j<ny;j++)
+		{	b[j] /= ny/2.;	b[j+ny] = 0;	}
+		mgl_fft(b, 1, ny, t->v, t->w[t->id], true);
+		for(j=0;j<ny;j++)	a[(i%nx)+nx*(j+ny*(i/nx))] = hypot(b[2*j], b[2*j+1]);
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_envz(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,nx=t->p[0],ny=t->p[1],nz=t->p[2],k=nx*ny;
+	double *b = t->b+2*nz*t->id;
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		for(j=0;j<nz;j++)	{	b[2*j] = a[j*k+i];	b[2*j+1] = 0;	}
+		mgl_fft(b, 1, nz, t->v, t->w[t->id], false);
+		for(j=0;j<nz;j++)
+		{	b[j] /= nz/2.;	b[j+nz] = 0;	}
+		mgl_fft(b, 1, nz, t->v, t->w[t->id], true);
+		for(j=0;j<nz;j++)	a[j*k+i] = hypot(b[2*j], b[2*j+1]);
+	}
+	return 0;
+}
+void MGL_EXPORT mgl_data_envelop(HMDT d, char dir)
+{
+	register long i;
+	long nx=d->nx,ny=d->ny,nz=d->nz,par[3]={nx,ny,nz};
+	if(mglNumThr<1)	mgl_set_num_thr(0);	// manually set number of threads
+	void *wt=0, *ws[mglNumThr];
+	double *b = 0;
+	if(dir=='x' && nx>1)
+	{
+		wt = mgl_fft_alloc(nx,ws,mglNumThr);
+		b = new double[2*nx*mglNumThr];
+		mglStartThreadT(mgl_envx,ny*nz,d->a,b,wt,ws,par);
+	}
+	if(dir=='y' && ny>1)
+	{
+		wt = mgl_fft_alloc(ny,ws,mglNumThr);
+		b = new double[2*ny*mglNumThr];
+		mglStartThreadT(mgl_envy,nx*nz,d->a,b,wt,ws,par);
+	}
+	if(dir=='z' && nz>1)
+	{
+		wt = mgl_fft_alloc(nz,ws,mglNumThr);
+		b = new double[2*nz*mglNumThr];
+		mglStartThreadT(mgl_envz,nx*ny,d->a,b,wt,ws,par);
+	}
+	for(i=0;i<nx*ny*nz;i++)	d->a[i] = hypot(b[2*i], b[2*i+1]);
+	if(b)	{	mgl_fft_free(wt,ws,mglNumThr);	delete []b;	}
+}
+//-----------------------------------------------------------------------------
+MGL_NO_EXPORT void* mgl_stfa1(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,k,ii,i0,mx=t->p[0],my=t->p[1],mz=t->p[2],dn=t->p[3],dd=dn/2,ny=t->p[4];
+	double *a = t->b+4*dn*t->id,ff;
+	mreal *d = (mreal*)t->a;
+	HCDT re = (HCDT)t->re, im = (HCDT)t->im;
+	for(ii=t->id;ii<t->n;ii+=mglNumThr)
+	{
+		i = ii%mx;	j = ii/mx;
+		for(k=0;k<2*dn;k++)
+		{
+			i0 = k-dd+j*dn;		ff = 1;
+			if(i0<0)	i0=0;	else if(i0>=ny)	i0=ny-1;
+			if(k<dd)
+			{	ff = 0.5*(k-dd/2.)/dd;		ff=0.5+ff*(3-ff*ff);	}
+			else if(k>=dn+dd)
+			{	ff = 0.5*(k-3.5*dd)/dd;	ff=0.5-ff*(3-ff*ff);	}
+			a[2*k] = re->v(i,i0)*ff;	a[2*k+1] = im->v(i,i0)*ff;
+		}
+		mgl_fft(a, 1, 2*dn, t->v, t->w[t->id], false);
+		for(k=0;k<dd;k++)
+		{
+			i0 = i+mx*(j+mz*k);
+			d[i0+mx*mz*dd] = hypot(a[4*k],a[4*k+1])/dn;
+			d[i0] = hypot(a[4*k+2*dn],a[4*k+2*dn+1])/dn;
+		}
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_stfa2(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,k,ii,i0,mx=t->p[0],my=t->p[1],mz=t->p[2],dn=t->p[3],dd=dn/2,nx=t->p[4];
+	double *a = t->b+4*dn*t->id,ff;
+	mreal *d = (mreal*)t->a;
+	HCDT re = (HCDT)t->re, im = (HCDT)t->im;
+	for(ii=t->id;ii<t->n;ii+=mglNumThr)
+	{
+		i = ii%my;	j = ii/mx;
+		for(k=0;k<2*dn;k++)
+		{
+			i0 = k-dd+i*dn;		ff = 1;
+			if(i0<0)	i0=0;	else if(i0>=nx)	i0=nx-1;
+			if(k<dd)
+			{	ff = 0.5*(k-dd/2.)/dd;	ff=0.5+ff*(3-ff*ff);	}
+			else if(k>=3*dd)
+			{	ff = 0.5*(k-3.5*dd)/dd;	ff=0.5-ff*(3-ff*ff);	}
+			a[2*k] = re->v(i0,j)*ff;	a[2*k+1] = im->v(i0,j)*ff;
+		}
+		mgl_fft(a, 1, 2*dn, t->v, t->w[t->id], false);
+		for(k=0;k<dd;k++)
+		{
+			i0 = i+my*(k+mx*j);
+			d[i0+dd*my] = hypot(a[4*k],a[4*k+1])/dn;
+			d[i0] = hypot(a[4*k+2*dn],a[4*k+2*dn+1])/dn;
+		}
+	}
+	return 0;
+}
+HMDT MGL_EXPORT mgl_data_stfa(HCDT re, HCDT im, long dn, char dir)
+{
+	mglData *d=new mglData;
+	if(dn<2)	return d;
+	dn = 2*(dn/2);
+	long nx = re->GetNx(), ny = re->GetNy();
+	if(nx*ny!=im->GetNx()*im->GetNy())	return d;
+	register long i,j,k,i0,dd=dn/2;
+	if(mglNumThr<1)	mgl_set_num_thr(0);	// manually set number of threads
+	double *a = new double[4*dn*mglNumThr],ff;
+	void *ws[mglNumThr], *wt = mgl_fft_alloc(2*dn,ws,mglNumThr);
+	long mx,my,mz;
+	if(dir=='y')
+	{
+		mx = nx;	my = dn;	mz = ny/dn;
+		mgl_data_create(d, mx, mz, my);
+		long par[5]={mx,my,mz,dn,ny};
+		mglStartThreadT(mgl_stfa1,mx*mz,d->a,a,wt,ws,par,re,im);
+	}
+	else
+	{
+		mx = dn;	my = nx/dn;	mz = ny;
+		mgl_data_create(d, my, mx, mz);
+		long par[5]={mx,my,mz,dn,nx};
+		mglStartThreadT(mgl_stfa2,my*mz,d->a,a,wt,ws,par,re,im);
+	}
+	delete []a;
+	mgl_fft_free(wt,ws,mglNumThr);
+	return d;
+}
+//-----------------------------------------------------------------------------
+MGL_NO_EXPORT void* mgl_sinx(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,k,nx=t->p[0];
+	double *b = t->b+2*nx*t->id, f=sqrt(2./nx);
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		k = i*nx;	memset(b,0,2*nx*sizeof(double));
+		for(j=1;j<nx;j++)	b[2*j]=sin(M_PI*j/nx)*(a[j+k]+a[nx-j+k])+(a[j+k]-a[nx-j+k])*0.5;
+		mgl_fft(b,1,nx,t->v,t->w[t->id],false);
+		a[k]=0;	a[k+1]=b[0]*f/2;	// fill sinfft
+		for(j=1;j<nx/2;j++)
+		{
+			a[k+2*j] = -b[2*j+1]*f;
+			a[k+2*j+1] = a[k+2*j-1]+b[2*j]*f;
+		}
+		if(nx%2)	a[nx-1] = -b[nx]*f;
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_siny(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long ii,i,j,k,nx=t->p[0],ny=t->p[1],nz=t->p[2];
+	double *b = t->b+2*ny*t->id, f=sqrt(2./ny);
+	mreal *a = (mreal*)t->a;
+	for(ii=t->id;ii<t->n;ii+=mglNumThr)
+	{
+		i = ii%nx;	k = ii/nx;	memset(b,0,2*ny*sizeof(double));
+		for(j=1;j<ny;j++)	b[2*j]=sin(M_PI*j/ny)*(a[i+nx*(ny*k+j)]+a[i+nx*(ny*k+ny-j)])+(a[i+nx*(ny*k+j)]-a[i+nx*(ny*k+ny-j)])*0.5;
+		mgl_fft(b,1,ny,t->v,t->w[t->id],false);
+		a[i+nx*ny*k]=0;	a[i+nx*(ny*k+1)]=b[0]*f/2;	// fill sinfft
+		for(j=1;j<ny/2;j++)
+		{
+			a[i+nx*(ny*k+2*j)] = -b[2*j+1]*f;
+			a[i+nx*(ny*k+2*j+1)] = a[i+nx*(ny*k+2*j-1)]+b[2*j]*f;
+		}
+		if(ny%2)	a[i+nx*(ny*k+ny-1)] = -b[ny]*f;
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_sinz(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,nx=t->p[0],ny=t->p[1],nz=t->p[2],k=nx*ny;
+	double *b = t->b+2*nz*t->id, f=sqrt(2./nz);
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		memset(b,0,2*nz*sizeof(double));
+		for(j=1;j<nz;j++)	b[2*j]=sin(M_PI*j/nz)*(a[i+k*j]+a[i+k*(nz-j)])+(a[i+k*j]-a[i+k*(nz-j)])*0.5;
+		mgl_fft(b,1,nz,t->v,t->w[t->id],false);
+		a[i]=0;	a[i+k]=b[0]*f/2;	// fill sinfft
+		for(j=1;j<nz/2;j++)
+		{
+			a[i+k*2*j] = -b[2*j+1]*f;
+			a[i+k*(2*j+1)] = a[i+k*(2*j-1)]+b[2*j]*f;
+		}
+		if(nz%2)	a[i+k*nz-k] = -b[nz]*f;
+	}
+	return 0;
+}
+void MGL_EXPORT mgl_data_sinfft(HMDT d, const char *dir)	// use DST-1
+{
+	if(!dir || *dir==0)	return;
+	double *b = 0;
+	if(mglNumThr<1)	mgl_set_num_thr(0);	// manually set number of threads
+	void *wt=0, *ws[mglNumThr];
+	long nx=d->nx, ny=d->ny, nz=d->nz;
+	long par[3]={nx,ny,nz}, i;
+	if(strchr(dir,'x') && nx>1)
+	{
+		wt = mgl_fft_alloc(nx,ws,mglNumThr);
+		b = new double[2*nx*mglNumThr];
+		mglStartThreadT(mgl_sinx,ny*nz,d->a,b,wt,ws,par);
+	}
+	if(strchr(dir,'y') && ny>1)
+	{
+		wt = mgl_fft_alloc(ny,ws,mglNumThr);
+		b = new double[2*ny*mglNumThr];
+		mglStartThreadT(mgl_siny,nx*nz,d->a,b,wt,ws,par);
+	}
+	if(strchr(dir,'z') && nz>1)
+	{
+		wt = mgl_fft_alloc(nz,ws,mglNumThr);
+		b = new double[2*nz*mglNumThr];
+		mglStartThreadT(mgl_sinz,nx*ny,d->a,b,wt,ws,par);
+	}
+	if(b)	{	mgl_fft_free(wt,ws,mglNumThr);	delete []b;	}
+}
+//-----------------------------------------------------------------------------
+MGL_NO_EXPORT void* mgl_cosx(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,k,nx=t->p[0],nn=nx-1;
+	double *b = t->b+2*nx*t->id, f=sqrt(2./nx);
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		k = i*nx;	memset(b,0,2*nx*sizeof(double));
+		for(j=0;j<nn;j++)	b[2*j]=(a[j+k]+a[nn-j+k])*0.5-sin(M_PI*j/nn)*(a[j+k]-a[nn-j+k]);
+		mgl_fft(b,1,nn,t->v,t->w[t->id],false);
+		double f1=0.5*(a[k]-a[nn+k]), s=-1;
+		a[nn+k]=0.5*(a[k]+a[nn+k]*(nn%2?-1:1));
+		for(j=1;j<nn;j++)
+		{
+			f1 += a[j+k]*cos(M_PI*j/nn);
+			a[nn+k] += a[j+k]*s;	s = -s;
+		}
+		a[k]=b[0]*f;	a[1+k]=f1*f;	a[nn+k]*=f;	// fill cosfft
+		for(j=1;j<nn/2;j++)
+		{
+			a[2*j+k] = b[2*j]*f;
+			a[2*j+1+k] = a[2*j-1+k]-b[2*j+1]*f;
+		}
+		if(nn%2)	a[nn-1+k] = b[nn-1]*f;
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_cosy(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long ii,i,j,k,nx=t->p[0],ny=t->p[1],nz=t->p[2],nn=ny-1;
+	double *b = t->b+2*ny*t->id, f=sqrt(2./ny);
+	mreal *a = (mreal*)t->a;
+	for(ii=t->id;ii<t->n;ii+=mglNumThr)
+	{
+		i = ii%nx;	k = ii/nx;	memset(b,0,2*ny*sizeof(double));
+		for(j=0;j<nn;j++)	b[2*j]=(a[i+nx*(ny*k+j)]+a[i+nx*(ny*k+nn-j)])*0.5-sin(M_PI*j/nn)*(a[i+nx*(ny*k+j)]-a[i+nx*(ny*k+nn-j)]);
+		mgl_fft(b,1,nn,t->v,t->w[t->id],false);
+		double f1=0.5*(a[i+nx*ny*k]-a[i+nx*(ny*k+nn)]), s=-1;
+		a[i+nx*(ny*k+nn)]=0.5*(a[i+nx*ny*k]+a[i+nx*(ny*k+nn)]*(nn%2?-1:1));
+		for(j=1;j<nn;j++)
+		{
+			f1 += a[i+nx*(ny*k+j)]*cos(M_PI*j/nn);
+			a[i+nx*(ny*k+nn)] += a[i+nx*(ny*k+j)]*s;	s = -s;
+		}
+		a[i+nx*ny*k]=b[0]*f;	a[i+nx*(ny*k+1)]=f1*f;	a[i+nx*(ny*k+nn)]*=f;	// fill cosfft
+		for(j=1;j<nn/2;j++)
+		{
+			a[i+nx*(ny*k+2*j)] = b[2*j]*f;
+			a[i+nx*(ny*k+2*j+1)] = a[i+nx*(ny*k+2*j-1)]-b[2*j+1]*f;
+		}
+		if(nn%2)	a[i+nx*(ny*k+nn-1)] = b[nn-1]*f;
+	}
+	return 0;
+}
+MGL_NO_EXPORT void* mgl_cosz(void *par)
+{
+	mglThreadT *t=(mglThreadT *)par;
+	register long i,j,nx=t->p[0],ny=t->p[1],nz=t->p[2],k=nx*ny,nn=nz-1;
+	double *b = t->b+2*nz*t->id, f=sqrt(2./nz);
+	mreal *a = (mreal*)t->a;
+	for(i=t->id;i<t->n;i+=mglNumThr)
+	{
+		memset(b,0,2*nz*sizeof(double));
+
+		for(j=0;j<nn;j++)	b[2*j]=(a[i+k*j]+a[i+k*(nn-j)])*0.5-sin(M_PI*j/nn)*(a[i+k*j]-a[i+k*(nn-j)]);
+		mgl_fft(b,1,nn,t->v,t->w[t->id],false);
+		double f1=0.5*(a[i]-a[i+k*nn]), s=-1;
+		a[i+k*nn]=0.5*(a[i]+a[i+k*nn]*(nn%2?-1:1));
+		for(j=1;j<nn;j++)
+		{
+			f1 += a[i+k*j]*cos(M_PI*j/nn);
+			a[i+k*nn] += a[i+k*j]*s;	s = -s;
+		}
+		a[i]=b[0]*f;	a[i+k]=f1*f;	a[i+k*nn]*=f;	// fill cosfft
+		for(j=1;j<nn/2;j++)
+		{
+			a[i+k*2*j] = b[2*j]*f;
+			a[i+k*2*j+k] = a[i+k*2*j-k]-b[2*j+1]*f;
+		}
+		if(nn%2)	a[i+k*nn-k] = b[nn-1]*f;
+	}
+	return 0;
+}
+void MGL_EXPORT mgl_data_cosfft(HMDT d, const char *dir)
+{
+	if(!dir || *dir==0)	return;
+	double *b = 0;
+	if(mglNumThr<1)	mgl_set_num_thr(0);	// manually set number of threads
+	void *wt=0, *ws[mglNumThr];
+	long nx=d->nx, ny=d->ny, nz=d->nz;
+	long par[3]={nx,ny,nz}, i;
+	if(strchr(dir,'x') && nx>1)
+	{
+		wt = mgl_fft_alloc(nx-1,ws,mglNumThr);
+		b = new double[2*nx*mglNumThr];
+		mglStartThreadT(mgl_cosx,ny*nz,d->a,b,wt,ws,par);
+	}
+	if(strchr(dir,'y') && ny>1)
+	{
+		wt = mgl_fft_alloc(ny-1,ws,mglNumThr);
+		b = new double[2*ny*mglNumThr];
+		mglStartThreadT(mgl_cosy,nx*nz,d->a,b,wt,ws,par);
+	}
+	if(strchr(dir,'z') && nz>1)
+	{
+		wt = mgl_fft_alloc(nz-1,ws,mglNumThr);
+		b = new double[2*nz*mglNumThr];
+		mglStartThreadT(mgl_cosz,nx*ny,d->a,b,wt,ws,par);
+	}
+	if(b)	{	mgl_fft_free(wt,ws,mglNumThr);	delete []b;	}
+}
+//-----------------------------------------------------------------------------
+HMDT MGL_EXPORT mgl_transform_a(HCDT am, HCDT ph, const char *tr)
+{
+	long nx = am->GetNx(), ny = am->GetNy(), nz = am->GetNz();
+	if(nx*ny*nz != ph->GetNx()*ph->GetNy()*ph->GetNz() || !tr || tr[0]==0)
+		return (new mglData);
+	mglData re(nx,ny,nz), im(nx,ny,nz);
+	const mglData *da=dynamic_cast<const mglData *>(am);
+	const mglData *dp=dynamic_cast<const mglData *>(ph);
+	if(da && dp)	for(long i=0;i<nx*ny*nz;i++)
+	{	re.a[i] = da->a[i]*cos(dp->a[i]);
+		im.a[i] = da->a[i]*sin(dp->a[i]);	}
+	else	for(long i=0;i<nx*ny*nz;i++)
+	{	re.a[i] = am->vthr(i)*cos(ph->vthr(i));
+		im.a[i] = am->vthr(i)*sin(ph->vthr(i));	}
+	return mgl_transform(&re, &im, tr);
+}
+//-----------------------------------------------------------------------------
+HMDT MGL_EXPORT mgl_transform(HCDT re, HCDT im, const char *tr)
+{
+	if(!tr || *tr==0)	return 0;
+	long nx = re->GetNx(), ny = re->GetNy(), nz = re->GetNz();
+	if(nx*ny*nz != im->GetNx()*im->GetNy()*im->GetNz() || !tr || tr[0]==0)
+		return (new mglData);
+	mglData rr(re),ii(im);
+	if(strchr(tr,'i') && strchr(tr,'f'))	// general case
+	{
+		if(tr[0]=='f')	mgl_data_fourier(&rr,&ii,"x");
+		if(tr[0]=='i')	mgl_data_fourier(&rr,&ii,"xi");
+		if(tr[1]=='f')	mgl_data_fourier(&rr,&ii,"y");
+		if(tr[1]=='i')	mgl_data_fourier(&rr,&ii,"yi");
+		if(tr[2]=='f')	mgl_data_fourier(&rr,&ii,"z");
+		if(tr[2]=='i')	mgl_data_fourier(&rr,&ii,"zi");
+	}
+	else if(strchr(tr,'f'))	// do Fourier only once for speeding up
+	{
+		char str[4] = "   ";
+		if(tr[0]=='f')	str[0]='x';
+		if(tr[1]=='f')	str[1]='y';
+		if(tr[2]=='f')	str[2]='z';
+		mgl_data_fourier(&rr,&ii,str);
+	}
+	else if(strchr(tr,'i'))	// do Fourier only once for speeding up
+	{
+		char str[5] = "   i";
+		if(tr[0]=='i')	str[0]='x';
+		if(tr[1]=='i')	str[1]='y';
+		if(tr[2]=='i')	str[2]='z';
+		mgl_data_fourier(&rr,&ii,str);
+	}
+	else if(strchr(tr,'s'))	// do Fourier only once for speeding up
+	{
+		if(tr[0]=='s')	{	rr.SinFFT("x");	ii.SinFFT("x");	}
+		if(tr[1]=='s')	{	rr.SinFFT("y");	ii.SinFFT("y");	}
+		if(tr[2]=='s')	{	rr.SinFFT("z");	ii.SinFFT("z");	}
+	}
+	else if(strchr(tr,'c'))	// do Fourier only once for speeding up
+	{
+		if(tr[0]=='c')	{	rr.CosFFT("x");	ii.CosFFT("x");	}
+		if(tr[1]=='c')	{	rr.CosFFT("y");	ii.CosFFT("y");	}
+		if(tr[2]=='c')	{	rr.CosFFT("z");	ii.CosFFT("z");	}
+	}
+	else if(strchr(tr,'h'))	// do Fourier only once for speeding up
+	{
+		if(tr[0]=='h')	{	rr.Hankel("x");	ii.Hankel("x");	}
+		if(tr[1]=='h')	{	rr.Hankel("y");	ii.Hankel("y");	}
+		if(tr[2]=='h')	{	rr.Hankel("z");	ii.Hankel("z");	}
+	}
+	mglData *d = new mglData(nx, ny, nz);
+	register long i;
+	for(i=0;i<nx*ny*nz;i++)	d->a[i] = hypot(rr.a[i],ii.a[i]);
+	return d;
+}
+//-----------------------------------------------------------------------------
+uintptr_t MGL_EXPORT mgl_transform_a_(uintptr_t *am, uintptr_t *ph, const char *tr, int l)
+{	char *s=new char[l+1];	memcpy(s,tr,l);	s[l]=0;
+	uintptr_t res = uintptr_t(mgl_transform_a(_DA_(am),_DA_(ph),s));
+	delete []s;		return res;	}
+uintptr_t MGL_EXPORT mgl_transform_(uintptr_t *re, uintptr_t *im, const char *tr, int l)
+{	char *s=new char[l+1];	memcpy(s,tr,l);	s[l]=0;
+	uintptr_t res = uintptr_t(mgl_transform(_DA_(re),_DA_(im),s));
+	delete []s;		return res;	}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_data_envelop_(uintptr_t *d, const char *dir, int)
+{	mgl_data_envelop(_DT_,*dir);	}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_datac_hankel(HADT d, const char *dir)
+{
+#if MGL_HAVE_GSL
+	if(!dir || *dir==0)	return;
+	double *ai=0, *af=0, *ag=0;
+	mreal mm;
+	gsl_dht *dht=0;
+	register long i,j,k;
+	long nx=d->nx, ny=d->ny, nz=d->nz;
+	dual *a=d->a;
+	if(strchr(dir,'x') && nx>1)
+	{
+		ai = new double[nx];	af = new double[nx];	ag = new double[nx];
+		dht = gsl_dht_new(nx,0,1);
+		mm = gsl_sf_bessel_zero_J0(nx+1);
+		for(i=0;i<ny*nz;i++)
+		{
+			for(j=0;j<nx;j++)	ai[j] = real(a[j+nx*i]);
+			gsl_dht_apply(dht,ai,af);
+			for(j=0;j<nx;j++)	ai[j] = imag(a[j+nx*i]);
+			gsl_dht_apply(dht,ai,ag);
+			for(j=0;j<nx;j++)	a[j+nx*i] = dual(af[j],ag[j])*mm;
+		}
+	}
+	if(strchr(dir,'y') && ny>1)
+	{
+		ai = new double[ny];	af = new double[ny];	ag = new double[ny];
+		dht = gsl_dht_new(ny,0,1);
+		mm = gsl_sf_bessel_zero_J0(ny+1);
+		for(i=0;i<nx;i++)	for(k=0;k<nz;k++)
+		{
+			for(j=0;j<ny;j++)	ai[j] = real(a[i+nx*(j+ny*k)]);
+			gsl_dht_apply(dht,ai,af);
+			for(j=0;j<ny;j++)	ai[j] = imag(a[i+nx*(j+ny*k)]);
+			gsl_dht_apply(dht,ai,ag);
+			for(j=0;j<ny;j++)	a[i+nx*(j+ny*k)] = dual(af[j],ag[j])*mm;
+		}
+	}
+	if(strchr(dir,'z') && nz>1)
+	{
+		ai = new double[nz];	af = new double[nz];	ag = new double[nz];
+		dht = gsl_dht_new(nz,0,1);
+		mm = gsl_sf_bessel_zero_J0(nz+1);
+		k = nx*ny;	for(i=0;i<k;i++)
+		{
+			for(j=0;j<nz;j++)	ai[j] = real(a[i+j*k]);
+			gsl_dht_apply(dht,ai,af);
+			for(j=0;j<nz;j++)	ai[j] = imag(a[i+j*k]);
+			gsl_dht_apply(dht,ai,ag);
+			for(j=0;j<nz;j++)	a[i+j*k] = dual(af[j],ag[j])*mm;
+		}
+	}
+	if(ai)	{	delete []ai;	delete []af;	gsl_dht_free(dht);	}
+#endif
+}
+void MGL_EXPORT mgl_datac_hankel_(uintptr_t *d, const char *dir,int l)
+{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
+	mgl_datac_hankel(_DC_,s);	delete []s;	}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_data_hankel(HMDT d, const char *dir)
+{
+#if MGL_HAVE_GSL
+	if(!dir || *dir==0)	return;
+	double *ai=0, *af=0, mm;
+	gsl_dht *dht=0;
+	register long i,j,k;
+	long nx=d->nx, ny=d->ny, nz=d->nz;
+	mreal *a=d->a;
+	if(strchr(dir,'x') && nx>1)
+	{
+		ai = new double[nx];	af = new double[nx];
+		dht = gsl_dht_new(nx,0,1);
+		mm = gsl_sf_bessel_zero_J0(nx+1);
+		for(i=0;i<ny*nz;i++)
+		{
+			for(j=0;j<nx;j++)	ai[j] = a[j+nx*i];
+			gsl_dht_apply(dht,ai,af);
+			for(j=0;j<nx;j++)	a[j+nx*i] = af[j]*mm;
+		}
+	}
+	if(strchr(dir,'y') && ny>1)
+	{
+		ai = new double[ny];	af = new double[ny];
+		dht = gsl_dht_new(ny,0,1);
+		mm = gsl_sf_bessel_zero_J0(ny+1);
+		for(i=0;i<nx;i++)	for(k=0;k<nz;k++)
+		{
+			for(j=0;j<ny;j++)	ai[j] = a[i+nx*(j+ny*k)];
+			gsl_dht_apply(dht,ai,af);
+			for(j=0;j<ny;j++)	a[i+nx*(j+ny*k)] = af[j]*mm;
+		}
+	}
+	if(strchr(dir,'z') && nz>1)
+	{
+		ai = new double[nz];	af = new double[nz];
+		dht = gsl_dht_new(nz,0,1);
+		mm = gsl_sf_bessel_zero_J0(nz+1);
+		k = nx*ny;	for(i=0;i<k;i++)
+		{
+			for(j=0;j<nz;j++)	ai[j] = a[i+j*k];
+			gsl_dht_apply(dht,ai,af);
+			for(j=0;j<nz;j++)	a[i+j*k] = af[j]*mm;
+		}
+	}
+	if(ai)	{	delete []ai;	delete []af;	gsl_dht_free(dht);	}
+#endif
+}
+void MGL_EXPORT mgl_data_hankel_(uintptr_t *d, const char *dir,int l)
+{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
+	mgl_data_hankel(_DT_,s);	delete []s;	}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_data_fill_sample(HMDT d, const char *how)
+{
+	if(!how || *how==0)	return;
+	bool xx = strchr(how,'x');
+	register long i,n=d->nx;
+	mreal *aa=d->a;
+	if(strchr(how,'h'))	// Hankel
+	{
+#if MGL_HAVE_GSL
+		gsl_dht *dht = gsl_dht_new(n,0,1);
+		for(i=0;i<n;i++)
+			aa[i] = xx ? gsl_dht_x_sample(dht, i) : gsl_dht_k_sample(dht, i);
+		gsl_dht_free(dht);
+#endif
+	}
+	else	// Fourier
+	{
+		if(xx)	for(i=0;i<n;i++)	aa[i] = mreal(2*i-n)/n;
+		else	for(i=0;i<n;i++)	aa[i] = M_PI*(i<n/2 ? i:i-n);
+	}
+	for(i=1;i<d->ny*d->nz;i++)	memcpy(aa+i*n,aa,n*sizeof(mreal));
+}
+void MGL_EXPORT mgl_data_fill_sample_(uintptr_t *d, const char *how,int l)
+{	char *s=new char[l+1];	memcpy(s,how,l);	s[l]=0;
+	mgl_data_fill_sample(_DT_,s);	delete []s;	}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_datac_fft_(uintptr_t *d, const char *dir, int l)
+{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
+	mgl_datac_fft(_DC_,s);	delete []s;	}
+void MGL_EXPORT mgl_data_fourier_(uintptr_t *re, uintptr_t *im, const char *dir, int l)
+{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
+	mgl_data_fourier(_DM_(re),_DM_(im),s);	delete []s;	}
+uintptr_t MGL_EXPORT mgl_data_stfa_(uintptr_t *re, uintptr_t *im, int *dn, char *dir, int)
+{	return uintptr_t(mgl_data_stfa(_DA_(re),_DA_(im),*dn,*dir));	}
+void MGL_EXPORT mgl_data_cosfft_(uintptr_t *d, const char *dir,int l)
+{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
+	mgl_data_cosfft(_DT_,s);	delete []s;	}
+void MGL_EXPORT mgl_data_sinfft_(uintptr_t *d, const char *dir,int l)
+{	char *s=new char[l+1];	memcpy(s,dir,l);	s[l]=0;
+	mgl_data_sinfft(_DT_,s);	delete []s;	}
+//-----------------------------------------------------------------------------
diff --git a/src/fit.cpp b/src/fit.cpp
index 8694448..e126789 100644
--- a/src/fit.cpp
+++ b/src/fit.cpp
@@ -35,7 +35,7 @@ void MGL_EXPORT mgl_puts_fit(HMGL gr, double x, double y, double z, const char *
 {
 	long n = strlen(mglFitRes)+(pre?strlen(pre):0)+1;
 	char *buf = new char[n];
-	if(pre)	sprintf(buf,"%s%s",pre,mglFitRes);
+	if(pre)	snprintf(buf,n,"%s%s",pre,mglFitRes);
 	else	strcpy(buf,mglFitRes);
 	mgl_puts(gr,x,y,z,buf,font,size);
 	delete []buf;
@@ -148,10 +148,10 @@ mreal MGL_NO_EXPORT mgl_fit_base(mglFitData *fd, mreal *ini)
 void mglPrepareFitEq(mglBase *gr,mreal chi, const char *eq, const char *var, mreal *par)
 {
 	char buf[32]="";
-	sprintf(mglFitRes,"chi=%g",chi);
+	snprintf(mglFitRes,1024,"chi=%g",chi);
 	for(int i=0;i<int(strlen(var));i++)
 	{
-		sprintf(buf,", %c=%g",var[i],par[i]);
+		snprintf(buf,32,", %c=%g",var[i],par[i]);
 		strcat(mglFitRes,buf);
 	}
 	gr->SetWarn(-1,mglFitRes);
@@ -163,7 +163,7 @@ void mglPrepareFitEq(mglBase *gr,mreal chi, const char *eq, const char *var, mre
 		const char *c = strchr(var,eq[i]);
 		if(c && (i==0 || !isalnum(eq[i-1])) && (i==len-1 || !isalnum(eq[i+1])))
 		{
-			sprintf(buf,"%g",par[c-var]);
+			snprintf(buf,32,"%g",par[c-var]);
 			strcat(mglFitRes+k, buf);	k+=strlen(buf);
 		}
 		else	{	mglFitRes[k] = eq[i];	k++;	}
diff --git a/src/font.cpp b/src/font.cpp
index 9ef672a..76cafe2 100644
--- a/src/font.cpp
+++ b/src/font.cpp
@@ -713,7 +713,7 @@ bool mglFont::Load(const char *base, const char *path)
 	}
 	Clear();							// first clear old
 
-	sprintf(str,"%s%c%s.vfm",path,sep,base?base:"");
+	snprintf(str,256,"%s%c%s.vfm",path,sep,base?base:"");
 	if(!(base && *base) || !read_main(str,cur))
 	{
 //		mglGlobalMess += "Load built-in font.\n";
@@ -722,7 +722,7 @@ bool mglFont::Load(const char *base, const char *path)
 	}
 
 	//================== bold ===========================================
-	sprintf(str,"%s%c%s_b.vfm",path,sep,base);	// this file may absent
+	snprintf(str,256,"%s%c%s_b.vfm",path,sep,base);	// this file may absent
 	if(read_data(str, fact+1, width[1], numl[1], ln[1], numt[1], tr[1], cur))
 	{
 		fact[3] = fact[1];		// copy default factor for bold-italic;
@@ -735,11 +735,11 @@ bool mglFont::Load(const char *base, const char *path)
 	}
 
 	//================== italic =========================================
-	sprintf(str,"%s%c%s_i.vfm",path,sep,base);
+	snprintf(str,256,"%s%c%s_i.vfm",path,sep,base);
 	read_data(str, fact+2, width[2], numl[2], ln[2], numt[2], tr[2], cur);
 
 	//================== bold-italic ====================================
-	sprintf(str,"%s%c%s_bi.vfm",path,sep,base);
+	snprintf(str,256,"%s%c%s_bi.vfm",path,sep,base);
 	read_data(str, fact+3, width[3], numl[3], ln[3], numt[3], tr[3], cur);
 	numb = cur;
 
diff --git a/src/mpi.cpp b/src/mpi.cpp
new file mode 100644
index 0000000..28b4c71
--- /dev/null
+++ b/src/mpi.cpp
@@ -0,0 +1,48 @@
+#include "mgl2/mpi.h"
+#include "mgl2/canvas.h"
+#include <mpi.h>
+#define MCW		MPI_COMM_WORLD
+#define TAG_DATA_Z	0
+#define TAG_DATA_C	1
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_mpi_send(HMGL gr, int id)
+{
+	mglCanvas *g = dynamic_cast<mglCanvas *>(gr);	if(!g)	return;
+	g->Finish();
+	long n = g->GetWidth()*g->GetHeight();
+	MPI_Send(g->Z,3*n,MPI_FLOAT,id,TAG_DATA_Z,MCW);
+	MPI_Send(g->C,12*n,MPI_CHAR,id,TAG_DATA_C,MCW);
+	MPI_Send(g->OI,n,MPI_INT,id,TAG_DATA_C,MCW);
+}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_mpi_recv(HMGL gr, int id)
+{
+	mglCanvas *g = dynamic_cast<mglCanvas *>(gr);	if(!g)	return;
+	g->Finish();
+	MPI_Status status;
+	long w = g->GetWidth(), h = g->GetHeight(), n = w*h;
+	float *zz = new float[3*n];
+	int *oi = new int[n];
+	unsigned char *cc = new unsigned char[12*n];
+	MPI_Recv(zz,3*n,MPI_FLOAT,id,TAG_DATA_Z,MCW,&status);
+	MPI_Recv(cc,12*n,MPI_CHAR,id,TAG_DATA_C,MCW,&status);
+	MPI_Recv(oi,n,MPI_INT,id,TAG_DATA_C,MCW,&status);
+	// TODO check status for errors
+	register long i,j,k;
+	for(k=0;k<n;k++)
+	{	// i0=x+Width*(Height-1-y)
+		i = k%w;	j = h-1-(k/w);
+		if(g->GetQuality()&2)
+		{
+			g->pnt_plot(i,j,zz[3*k+2],cc+12*k+8,oi[k]);
+			g->pnt_plot(i,j,zz[3*k+1],cc+12*k+4,oi[k]);
+		}
+		g->pnt_plot(i,j,zz[3*k],cc+12*k,oi[k]);
+	}
+	g->set(MGL_FINISHED);
+	delete []oi; 	delete []zz; 	delete []cc;
+}
+//-----------------------------------------------------------------------------
+void MGL_EXPORT mgl_mpi_send_(uintptr_t *gr, int *id)	{	mgl_mpi_send(_GR_, *id);	}
+void MGL_EXPORT mgl_mpi_recv_(uintptr_t *gr, int *id)	{	mgl_mpi_recv(_GR_, *id);	}
+//-----------------------------------------------------------------------------
diff --git a/src/opengl.cpp b/src/opengl.cpp
index 233e7cc..66bf29f 100644
--- a/src/opengl.cpp
+++ b/src/opengl.cpp
@@ -41,11 +41,11 @@ void mglCanvasGL::Finish(bool fast)
 			p=Prm[i];	PDef=p.n3;	pPos=p.s;	PenWidth=p.w;
 			switch(p.type)
 			{
-			case 0:	mark_draw(p.n1,p.n4,p.s,0);	break;
-			case 1:	line_draw(p.n1,p.n2,0);		break;
-			case 2:	trig_draw(p.n1,p.n2,p.n3,true,0);	break;
-			case 3:	quad_draw(p.n1,p.n2,p.n3,p.n4,0);	break;
-			case 4:	glyph_draw(&p,0);	break;
+			case 0:	mark_draw(Pnt[p.n1],p.n4,p.s,0);	break;
+			case 1:	line_draw(p.n1,p.n2);	break;
+			case 2:	trig_draw(p.n1,p.n2,p.n3);	break;
+			case 3:	quad_draw(p.n1,p.n2,p.n3,p.n4);	break;
+			case 4:	glyph_draw(p,0);	break;
 			}
 		}
 		PDef=pdef;	pPos=ss;	PenWidth=ww;
@@ -231,26 +231,24 @@ unsigned char **mglCanvasGL::GetRGBLines(long &width, long &height, unsigned cha
 	return p;
 }
 //-----------------------------------------------------------------------------
-void mglCanvasGL::trig_draw(long k1, long k2, long k3, bool, mglDrawReg *)
+void mglCanvasGL::quad_draw(long k1, long k2, long k3, long k4)
 {
-	if(k1<0 || k2<0 || k3<0)	return;
-	glBegin(GL_TRIANGLES);
-	glArrayElement(k1);	glArrayElement(k2);	glArrayElement(k3);
+	glBegin(GL_QUADS);
+	glArrayElement(k1);	glArrayElement(k2);
+	glArrayElement(k4);	glArrayElement(k3);
 	glEnd();
 }
 //-----------------------------------------------------------------------------
-void mglCanvasGL::quad_draw(long k1, long k2, long k3, long k4, mglDrawReg *)
+void mglCanvasGL::trig_draw(long k1, long k2, long k3)
 {
-	if(k1<0 || k2<0 || k3<0 || k4<0)	return;
-	glBegin(GL_QUADS);
-	glArrayElement(k1);	glArrayElement(k2);
-	glArrayElement(k4);	glArrayElement(k3);
+	glBegin(GL_TRIANGLES);
+	glArrayElement(k1);	glArrayElement(k2);	glArrayElement(k3);
 	glEnd();
 }
 //-----------------------------------------------------------------------------
-void mglCanvasGL::line_draw(long k1, long k2, mglDrawReg *)
+void mglCanvasGL::line_draw(long k1, long k2)
 {
-	if(k1<0 || k2<0 || PDef==0)	return;
+	if(PDef==0)	return;
 /*	unsigned pdef = PDef*0x10001;
 	pdef = pdef << (int(100*pPos+0.5)%16);
 	set_pen(pdef&0xffff,PenWidth);*/
@@ -260,11 +258,39 @@ void mglCanvasGL::line_draw(long k1, long k2, mglDrawReg *)
 	glEnd();
 }
 //-----------------------------------------------------------------------------
-void mglCanvasGL::pnt_draw(long k1, mglDrawReg *)
+void mglCanvasGL::quad_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, const mglPnt &p4, mglDrawReg *)
+{
+	glBegin(GL_QUADS);
+	glNormal3f(p1.u,p1.v,p1.w);	glColor4f(p1.r,p1.g,p1.b,p1.a);	glVertex3f(p1.x,p1.y,p1.z);
+	glNormal3f(p2.u,p2.v,p2.w);	glColor4f(p2.r,p2.g,p2.b,p2.a);	glVertex3f(p2.x,p2.y,p2.z);
+	glNormal3f(p3.u,p3.v,p3.w);	glColor4f(p3.r,p3.g,p3.b,p3.a);	glVertex3f(p3.x,p3.y,p3.z);
+	glNormal3f(p4.u,p4.v,p4.w);	glColor4f(p4.r,p4.g,p4.b,p4.a);	glVertex3f(p4.x,p4.y,p4.z);
+	glEnd();
+}
+//-----------------------------------------------------------------------------
+void mglCanvasGL::trig_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, bool, mglDrawReg *)
+{
+	glBegin(GL_TRIANGLES);
+	glNormal3f(p1.u,p1.v,p1.w);	glColor4f(p1.r,p1.g,p1.b,p1.a);	glVertex3f(p1.x,p1.y,p1.z);
+	glNormal3f(p2.u,p2.v,p2.w);	glColor4f(p2.r,p2.g,p2.b,p2.a);	glVertex3f(p2.x,p2.y,p2.z);
+	glNormal3f(p3.u,p3.v,p3.w);	glColor4f(p3.r,p3.g,p3.b,p3.a);	glVertex3f(p3.x,p3.y,p3.z);
+	glEnd();
+}
+//-----------------------------------------------------------------------------
+void mglCanvasGL::line_draw(const mglPnt &p1, const mglPnt &p2, mglDrawReg *)
+{
+	if(PDef==0)	return;
+	set_pen(PDef,PenWidth);
+	glBegin(GL_LINES);
+	glColor4f(p1.r,p1.g,p1.b,p1.a);	glVertex3f(p1.x,p1.y,p1.z);
+	glColor4f(p2.r,p2.g,p2.b,p2.a);	glVertex3f(p2.x,p2.y,p2.z);
+	glEnd();
+}
+//-----------------------------------------------------------------------------
+void mglCanvasGL::pnt_draw(const mglPnt &p1, mglDrawReg *)
 {
-	if(k1<0)	return;
 	glBegin(GL_POINTS);
-	glArrayElement(k1);
+	glColor4f(p1.r,p1.g,p1.b,p1.a);	glVertex3f(p1.x,p1.y,p1.z);
 	glEnd();
 }
 //-----------------------------------------------------------------------------
diff --git a/src/parser.cpp b/src/parser.cpp
index f836869..8924f7a 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -583,7 +583,7 @@ int mglParser::Parse(mglGraph *gr, const wchar_t *string, long pos)
 				res = 0;	str +=2;	mgl_wcstrim(str);
 				mreal d = mglFormulaCalc(str, this).a[0];
 				char *buf=new char[128];
-				sprintf(buf,"%g",d);
+				snprintf(buf,128,"%g",d);
 				AddParam(nn, buf);	delete []buf;
 			}
 			delete []s;	return res;
@@ -679,7 +679,7 @@ int mglParser::Parse(mglGraph *gr, const wchar_t *string, long pos)
 				if(n && k!=na+2)
 				{
 					char buf[64];
-					sprintf(buf,"Bad arguments for %ls: %ld instead of %d\n",
+					snprintf(buf,64,"Bad arguments for %ls: %ld instead of %d\n",
 							a[0].w.c_str(),k-2,na);
 					gr->SetWarn(-1,buf);	n = 1;
 				}
@@ -794,8 +794,8 @@ int mglParser::ParseDat(mglGraph *gr, const wchar_t *string, mglData &res)
 			a[i].s.assign(a[i].w.begin(),a[i].w.end());
 		}
 		mglCommand *rts=FindCommand(arg[0]);
-		if(!rts || rts->type!=4)	return 2;
-		n = rts->exec(gr, k, a, kk.c_str(), 0);
+		if(!rts || rts->type!=4)	n = 2;
+		else n = rts->exec(gr, k, a, kk.c_str(), 0);
 		delete []a;
 	}
 	delete []s;	return n;
@@ -927,11 +927,11 @@ void mglParser::Execute(mglGraph *gr, int n, const wchar_t **text)
 		gr->SetObjId(i+1);
 		r = Parse(gr,text[i],i+1);
 		if(r<0)	{	i = -r-2;	continue;	}
-		if(r==1)		sprintf(buf,"\nWrong argument(s) in line %ld\n", i+1);
-		else if(r==2)	sprintf(buf,"\nWrong command in line %ld\n", i+1);
-		else if(r==3)	sprintf(buf,"\nString too long in line %ld\n", i+1);
-		else if(r==4)	sprintf(buf,"\nUnbalanced ' in line %ld\n", i+1);
-		else if(gr->GetWarn()>0)	sprintf(buf," in line %ld\n", i+1);
+		if(r==1)		snprintf(buf,64,"\nWrong argument(s) in line %ld\n", i+1);
+		else if(r==2)	snprintf(buf,64,"\nWrong command in line %ld\n", i+1);
+		else if(r==3)	snprintf(buf,64,"\nString too long in line %ld\n", i+1);
+		else if(r==4)	snprintf(buf,64,"\nUnbalanced ' in line %ld\n", i+1);
+		else if(gr->GetWarn()>0)	snprintf(buf,64," in line %ld\n", i+1);
 		else *buf=0;
 		if(*buf)	gr->SetWarn(-2,buf);
 	}
diff --git a/src/pde.cpp b/src/pde.cpp
index 4442218..9fb7a6c 100644
--- a/src/pde.cpp
+++ b/src/pde.cpp
@@ -22,9 +22,6 @@
 #include <complex>
 #define dual	std::complex<double>
 #define GAMMA	0.1
-#if MGL_HAVE_GSL
-#include <gsl/gsl_fft_complex.h>
-#endif
 //-----------------------------------------------------------------------------
 struct mgl_pde_ham
 {
@@ -74,7 +71,7 @@ MGL_NO_EXPORT void *mgl_pde_hprep(void *par)
 	}
 	return 0;
 }
-// Solve equation du/dz = ham(p,q,x,y,z,|u|)[u] where p=d/dx, q=d/dy. At this moment simplified form of ham is supported: ham = f(p,q,z) + g(x,y,z,'u'), where variable 'u'=|u| (for allowing solve nonlinear problems). You may specify imaginary part like ham = p^2 + i*x*(x>0) but only if dependence on variable 'i' is linear (i.e. ham = hre+i*him).
+// Solve equation dx/dz = func(p,q,x,y,z,|u|)[u] where p=d/dx, q=d/dy. At this moment simplified form of ham is supported: ham = f(p,q,z) + g(x,y,z,'u'), where variable 'u'=|u| (for allowing solve nonlinear problems). You may specify imaginary part like ham = p^2 + i*x*(x>0) but only if dependence on variable 'i' is linear (i.e. ham = hre+i*him).
 HMDT MGL_EXPORT mgl_pde_solve(HMGL gr, const char *ham, HCDT ini_re, HCDT ini_im, mreal dz, mreal k0, const char *opt)
 {
 	gr->SaveState(opt);
@@ -87,7 +84,7 @@ HMDT MGL_EXPORT mgl_pde_solve(HMGL gr, const char *ham, HCDT ini_re, HCDT ini_im
 	if(ini_im->GetNx()*ini_im->GetNy() != nx*ny)// Wrong dimensions
 	{	gr->SetWarn(mglWarnDim,"PDE");	return res;	}
 	mgl_data_create(res, nz, nx, ny);
-#if MGL_HAVE_GSL
+
 	mglFormula eqs(ham);
 	dual *a = new dual[4*nx*ny], hh0;	// Add "damping" area
 	dual *hxy = new dual[4*nx*ny], *hxv = new dual[4*nx*ny];
@@ -125,16 +122,13 @@ HMDT MGL_EXPORT mgl_pde_solve(HMGL gr, const char *ham, HCDT ini_re, HCDT ini_im
 	tmp.yy = Min.y-dy*(ny/2);	tmp.ys = ys;	tmp.dy = dy;	tmp.dq = dq;
 
 	// prepare fft. NOTE: slow procedures due to unknown nx, ny.
-	gsl_fft_complex_wavetable *wtx = gsl_fft_complex_wavetable_alloc(2*nx);
-	gsl_fft_complex_workspace *wsx = gsl_fft_complex_workspace_alloc(2*nx);
-	gsl_fft_complex_wavetable *wty = gsl_fft_complex_wavetable_alloc(2*ny);
-	gsl_fft_complex_workspace *wsy = gsl_fft_complex_workspace_alloc(2*ny);
+	void *wsx, *wtx = mgl_fft_alloc(2*nx,&wsx,1);
+	void *wsy, *wty = mgl_fft_alloc(2*ny,&wsy,1);
 	for(k=1;k<nz;k++)
 	{
 		if(gr->Stop)
 		{
-			gsl_fft_complex_workspace_free(wsx);	gsl_fft_complex_wavetable_free(wtx);
-			gsl_fft_complex_workspace_free(wsy);	gsl_fft_complex_wavetable_free(wty);
+			mgl_fft_free(wtx,&wsx,1);	mgl_fft_free(wty,&wsy,1);
 			delete []a;		delete []dmp;	delete []hxy;	delete []hxv;
 			delete []huy;	delete []huv;	delete []hx;	delete []hy;
 			delete []hu;	delete []hv;	return 0;
@@ -155,79 +149,70 @@ HMDT MGL_EXPORT mgl_pde_solve(HMGL gr, const char *ham, HCDT ini_re, HCDT ini_im
 		}
 		// solve equation
 		for(i=0;i<4*nx*ny;i++)	a[i] *= exp(hxy[i])*exp(-double(dmp[i]*dz))/ff;
-		for(i=0;i<2*ny;i++)		gsl_fft_complex_transform((double *)(a+i*2*nx), 1, 2*nx, wtx, wsx, forward);
+		for(i=0;i<2*ny;i++)		mgl_fft((double *)(a+i*2*nx), 1, 2*nx, wtx, wsx, false);
 		for(i=0;i<4*nx*ny;i++)	a[i] *= exp(huy[i]);
-		if(ny>1) for(i=0;i<2*nx;i++)	gsl_fft_complex_transform((double *)(a+i), 2*nx, 2*ny, wty, wsy, forward);
+		if(ny>1) for(i=0;i<2*nx;i++)	mgl_fft((double *)(a+i), 2*nx, 2*ny, wty, wsy, false);
 		for(i=0;i<4*nx*ny;i++)	a[i] *= exp(huv[i]);
-		for(i=0;i<2*ny;i++)		gsl_fft_complex_transform((double *)(a+2*i*nx), 1, 2*nx, wtx, wsx, backward);
+		for(i=0;i<2*ny;i++)		mgl_fft((double *)(a+2*i*nx), 1, 2*nx, wtx, wsx, true);
 		for(i=0;i<4*nx*ny;i++)	a[i] *= exp(hxv[i]);
-		if(ny>1) for(i=0;i<2*nx;i++)	gsl_fft_complex_transform((double *)(a+i), 2*nx, 2*ny, wty, wsy, backward);
+		if(ny>1) for(i=0;i<2*nx;i++)	mgl_fft((double *)(a+i), 2*nx, 2*ny, wty, wsy, true);
 		for(i=0;i<nx;i++)	for(j=0;j<ny;j++)	// save result
 		{
 			i0 = i+nx/2+2*nx*(j+ny/2);
 			res->a[k+nz*(i+nx*j)] = abs(a[i0]);
 		}
 	}
-	gsl_fft_complex_workspace_free(wsx);
-	gsl_fft_complex_wavetable_free(wtx);
-	gsl_fft_complex_workspace_free(wsy);
-	gsl_fft_complex_wavetable_free(wty);
+	mgl_fft_free(wtx,&wsx,1);	mgl_fft_free(wty,&wsy,1);
 	delete []a;		delete []dmp;
 	delete []hxy;	delete []hxv;	delete []huy;	delete []huv;
 	delete []hx;	delete []hy;	delete []hu;	delete []hv;
-#endif
 	gr->LoadState();
 	return res;
 }
 //-----------------------------------------------------------------------------
-// Solve GO ray equation like dr/dt = d ham/dp, dp/dt = -d ham/dr where ham = ham(x,y,z,p,q,v,t) and px=p, py=q, pz=v. The starting point (at t=0) is r0, p0. Result is array of {x,y,z,p,q,v,t}
-HMDT MGL_EXPORT mgl_ray_trace(const char *ham, mreal x0, mreal y0, mreal z0, mreal px, mreal py, mreal pz, mreal dt, mreal tmax)
+HMDT MGL_EXPORT mgl_ode_solve(void (*func)(const mreal *x, mreal *dx, void *par), int n, mreal *x0, mreal dt, mreal tmax, void *par)
 {
 	mglData *res=new mglData;
 	if(tmax<dt)	return res;	// nothing to do
 	int nt = int(tmax/dt)+1;
-	mgl_data_create(res,7,nt,1);
-	mgl_data_set_id(res,"xyzpqvt");
-#if MGL_HAVE_GSL
-	mreal x[6], k1[6], k2[6], k3[6], hh=dt/2;
-	mglFormula eqs(ham);
+	mgl_data_create(res,n,nt,1);
+	mreal x[n], k1[n], k2[n], k3[n], v[n], hh=dt/2;
+	register long i,k;
 	// initial conditions
-	x[0] = res->a[0] = x0;	x[1] = res->a[1] = y0;	x[2] = res->a[2] = z0;
-	x[3] = res->a[3] = px;	x[4] = res->a[4] = py;	x[5] = res->a[5] = pz;
-	res->a[6] = 0;
+	for(i=0;i<n;i++)	x[i] = res->a[i] = x0[i];
 	// Runge Kutta scheme of 4th order
-	char v[7]="xyzpqv";
-	mreal var[MGL_VS];	memset(var,0,MGL_VS*sizeof(mreal));
-	register int i,k;
 	for(k=1;k<nt;k++)
 	{
-		// 		md->H(cy,k1);
-		var['t'-'a']=k*dt;		for(i=0;i<6;i++)	var[v[i]-'a'] = x[i];
-		k1[0] = eqs.CalcD(var,'p');	k1[3] = -eqs.CalcD(var,'x');
-		k1[1] = eqs.CalcD(var,'q');	k1[4] = -eqs.CalcD(var,'y');
-		k1[2] = eqs.CalcD(var,'v');	k1[5] = -eqs.CalcD(var,'z');
-		// 		ty = cy/(k1*hh);	md->H(ty,k2);
-		var['t'-'a']=k*dt+hh;	for(i=0;i<6;i++)	var[v[i]-'a'] = x[i]+k1[i]*hh;
-		k2[0] = eqs.CalcD(var,'p');	k2[3] = -eqs.CalcD(var,'x');
-		k2[1] = eqs.CalcD(var,'q');	k2[4] = -eqs.CalcD(var,'y');
-		k2[2] = eqs.CalcD(var,'v');	k2[5] = -eqs.CalcD(var,'z');
-		//		ty = cy/(k2*hh);	md->H(ty,k3);
-		var['t'-'a']=k*dt+hh;	for(i=0;i<6;i++)	var[v[i]-'a'] = x[i]+k2[i]*hh;
-		k3[0] = eqs.CalcD(var,'p');	k3[3] = -eqs.CalcD(var,'x');
-		k3[1] = eqs.CalcD(var,'q');	k3[4] = -eqs.CalcD(var,'y');
-		k3[2] = eqs.CalcD(var,'v');	k3[5] = -eqs.CalcD(var,'z');
-		//		ty = cy/(k2*h);	k3+=k2;	md->H(ty,k2);
-		var['t'-'a']=k*dt+dt;	for(i=0;i<6;i++)
-		{	var[v[i]-'a'] = x[i]+k3[i]*dt;	k3[i] += k2[i];	}
-		k2[0] = eqs.CalcD(var,'p');	k2[3] = -eqs.CalcD(var,'x');
-		k2[1] = eqs.CalcD(var,'q');	k2[4] = -eqs.CalcD(var,'y');
-		k2[2] = eqs.CalcD(var,'v');	k2[5] = -eqs.CalcD(var,'z');
-		//		cy /= (k1+k2+k3*2.)*(h/6);
-		for(i=0;i<6;i++)
-			res->a[i+7*k] = x[i] += (k1[i]+k2[i]+2*k3[i])*dt/6;
-		res->a[6+7*k] = dt*k;
+		func(x,k1,par);
+		for(i=0;i<n;i++)	v[i] = x[i]+k1[i]*hh;
+		func(v,k2,par);
+		for(i=0;i<n;i++)	v[i] = x[i]+k2[i]*hh;
+		func(v,k3,par);
+		for(i=0;i<n;i++)	{	v[i] = x[i]+k3[i]*dt;	k3[i] += k2[i];	}
+		func(v,k2,par);
+		for(i=0;i<n;i++)	res->a[i+n*k] = x[i] += (k1[i]+k2[i]+2*k3[i])*dt/6;
 	}
-#endif
+	return res;
+}
+//-----------------------------------------------------------------------------
+void MGL_NO_EXPORT mgl_ray3d(const mreal *in, mreal *out, void *par)
+{
+	mglFormula *eqs = (mglFormula *)par;
+	const char *v="xyzpqvt";
+	mreal var[MGL_VS];	memset(var,0,MGL_VS*sizeof(mreal));
+	for(int i=0;i<7;i++)	var[v[i]-'a'] = in[i];
+	out[0] = eqs->CalcD(var,'p');	out[3] = -eqs->CalcD(var,'x');
+	out[1] = eqs->CalcD(var,'q');	out[4] = -eqs->CalcD(var,'y');
+	out[2] = eqs->CalcD(var,'v');	out[5] = -eqs->CalcD(var,'z');
+	out[7] = eqs->CalcD(var,'i');	out[6] = 1;
+}
+// Solve GO ray equation like dr/dt = d ham/dp, dp/dt = -d ham/dr where ham = ham(x,y,z,p,q,v,t) and px=p, py=q, pz=v. The starting point (at t=0) is r0, p0. Result is array of {x,y,z,p,q,v,t}
+HMDT MGL_EXPORT mgl_ray_trace(const char *ham, mreal x0, mreal y0, mreal z0, mreal px, mreal py, mreal pz, mreal dt, mreal tmax)
+{
+	mglFormula eqs(ham);
+	mreal in[8]={x0,y0,z0,px,py,pz,0,0};
+	HMDT res = mgl_ode_solve(mgl_ray3d,8,in,dt,tmax,&eqs);
+	mgl_data_set_id(res,"xyzpqvti");
 	return res;
 }
 //-----------------------------------------------------------------------------
@@ -238,18 +223,18 @@ struct mgl_ap
 	mgl_ap()	{	memset(this,0,sizeof(mgl_ap));	}
 };
 //-----------------------------------------------------------------------------
-void MGL_NO_EXPORT mgl_init_ra(int n, const mreal *r, mgl_ap *ra)	// prepare some intermediate data for QO (3d case)
+void MGL_NO_EXPORT mgl_init_ra(int n, int n7, const mreal *r, mgl_ap *ra)	// prepare some intermediate data for QO (3d case)
 {
 	register double tt;
-	tt = hypot(r[7]-r[0], r[8]-r[1]);
+	tt = hypot(r[n7]-r[0], r[n7+1]-r[1]);
 	if(tt)
 	{
-		ra[0].x1 = (r[8]-r[1])/tt;
-		ra[0].y1 = (r[0]-r[7])/tt;
+		ra[0].x1 = (r[n7+1]-r[1])/tt;
+		ra[0].y1 = (r[0]-r[n7])/tt;
 		ra[0].z1 = 0;
 	}
 	else	{	ra[0].x1 = ra[0].y1 = 0;	ra[0].z1 = 1;	}
-	ra[0].x0 = r[7] - r[0];	ra[0].y0 = r[8] - r[1];	ra[0].z0 = r[9] - r[2];
+	ra[0].x0 = r[n7] - r[0];	ra[0].y0 = r[n7+1] - r[1];	ra[0].z0 = r[n7+2] - r[2];
 	tt = sqrt(ra[0].x0*ra[0].x0 + ra[0].y0*ra[0].y0 + ra[0].z0*ra[0].z0);
 	ra[0].x0 /= tt;	ra[0].y0 /= tt;	ra[0].z0 /= tt;
 	ra[0].x2 = ra[0].y1*ra[0].z0 - ra[0].y0*ra[0].z1;	// vector g_2
@@ -258,10 +243,10 @@ void MGL_NO_EXPORT mgl_init_ra(int n, const mreal *r, mgl_ap *ra)	// prepare som
 	register long i;
 	for(i=1;i<n;i++)	// NOTE: no parallel due to dependence on prev point!
 	{
-		ra[i].dt = r[6+7*i] - r[7*i-1];
-		ra[i].x0 = r[7*i] - r[7*i-7];	// NOTE: very rough formulas
-		ra[i].y0 = r[7*i+1] - r[7*i-6];	// for corresponding with dt one
-		ra[i].z0 = r[7*i+2] - r[7*i-5];	// for corresponding with dt one
+		ra[i].dt = r[6+n7*i] - r[6+n7*(i-1)];
+		ra[i].x0 = r[n7*i]   - r[n7*(i-1)];		// NOTE: very rough formulas
+		ra[i].y0 = r[n7*i+1] - r[n7*(i-1)+1];	// for corresponding with dt one
+		ra[i].z0 = r[n7*i+2] - r[n7*(i-1)+2];	// for corresponding with dt one
 		tt = sqrt(ra[i].x0*ra[i].x0 + ra[i].y0*ra[i].y0 + ra[i].z0*ra[i].z0);
 		ra[i].x0 /= tt;	ra[i].y0 /= tt;	ra[i].z0 /= tt;
 		ra[i].ch = tt/ra[i].dt;
@@ -278,9 +263,9 @@ void MGL_NO_EXPORT mgl_init_ra(int n, const mreal *r, mgl_ap *ra)	// prepare som
 		tt = ra[i].x0*ra[i-1].x2 + ra[i].y0*ra[i-1].y2 + ra[i].z0*ra[i-1].z2;
 		ra[i].t2 = tt/(ra[i].dt*ra[i].ch);
 		// other parameters
-		ra[i].pt = r[7*i+3]*ra[i].x0 + r[7*i+4]*ra[i].y0 + r[7*i+5]*ra[i].z0;
-		ra[i].q1 = r[7*i+3]*ra[i].x1 + r[7*i+4]*ra[i].y1 + r[7*i+5]*ra[i].z1;
-		ra[i].q2 = r[7*i+3]*ra[i].x2 + r[7*i+4]*ra[i].y2 + r[7*i+5]*ra[i].z2;
+		ra[i].pt = r[n7*i+3]*ra[i].x0 + r[n7*i+4]*ra[i].y0 + r[n7*i+5]*ra[i].z0;
+		ra[i].q1 = r[n7*i+3]*ra[i].x1 + r[n7*i+4]*ra[i].y1 + r[n7*i+5]*ra[i].z1;
+		ra[i].q2 = r[n7*i+3]*ra[i].x2 + r[n7*i+4]*ra[i].y2 + r[n7*i+5]*ra[i].z2;
 		ra[i].d1 = (ra[i].q1-ra[i-1].q1)/(ra[i].dt*ra[i].ch);
 		ra[i].d2 = (ra[i].q2-ra[i-1].q2)/(ra[i].dt*ra[i].ch);
 	}
@@ -337,13 +322,13 @@ HMDT MGL_EXPORT mgl_qo2d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal px, m
 	mglData *res=new mglData;
 	const mglData *ray=dynamic_cast<const mglData *>(ray_dat);	// NOTE: Ray must be mglData!
 	if(!ray)	return res;
-	int nx=ini_re->GetNx(), nt=ray->ny;
+	int nx=ini_re->GetNx(), nt=ray->ny, n7=ray->nx;
 	if(nx<2 || ini_im->GetNx()!=nx || nt<2)	return res;
 	mgl_data_create(res,nx,nt,1);
-#if MGL_HAVE_GSL
+
 	dual *a=new dual[2*nx], *hu=new dual[2*nx],  *hx=new dual[2*nx];
 	double *dmp=new double[2*nx];
-	mgl_ap *ra = new mgl_ap[nt];	mgl_init_ra(ray->ny, ray->a, ra);	// ray
+	mgl_ap *ra = new mgl_ap[nt];	mgl_init_ra(nt, n7, ray->a, ra);	// ray
 	register int i;
 
 	mreal dr = r/(nx-1), dk = M_PI*(nx-1)/(k0*r*nx), tt, x1, hh, B1, pt0;
@@ -355,8 +340,7 @@ HMDT MGL_EXPORT mgl_qo2d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal px, m
 		dmp[2*nx-1-i] = dmp[i] = 30*GAMMA*x1*x1/k0;
 	}
 	for(i=0;i<nx;i++)	a[i+nx/2] = dual(ini_re->v(i),ini_im->v(i));	// init
-	gsl_fft_complex_wavetable *wtx = gsl_fft_complex_wavetable_alloc(2*nx);
-	gsl_fft_complex_workspace *wsx = gsl_fft_complex_workspace_alloc(2*nx);
+	void *wsx, *wtx = mgl_fft_alloc(2*nx,&wsx,1);
 	if(xx && yy)	{	xx->Create(nx,nt);	yy->Create(nx,nt);	}
 
 	mgl_qo2d_ham tmp;	// parameters for Hamiltonian calculation
@@ -370,33 +354,33 @@ HMDT MGL_EXPORT mgl_qo2d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal px, m
 		if(xx && yy)	for(i=0;i<nx;i++)	// prepare xx, yy
 		{
 			x1 = (2*i-nx+1)*dr;
-			xx->a[i+k*nx] = ray->a[7*k] + ra[k].x1*x1;	// new coordinates
-			yy->a[i+k*nx] = ray->a[7*k+1] + ra[k].y1*x1;
+			xx->a[i+k*nx] = ray->a[n7*k] + ra[k].x1*x1;	// new coordinates
+			yy->a[i+k*nx] = ray->a[n7*k+1] + ra[k].y1*x1;
 		}
-		tmp.r=ray->a+7*k;	tmp.ra=ra+k;
+		tmp.r=ray->a+n7*k;	tmp.ra=ra+k;
 		hh = ra[k].pt*(1/sqrt(sqrt(1.041))-1);	// 0.041=0.45^4 -- minimal value of h
 		tmp.h0 = ham(0, tmp.r[0], tmp.r[1], tmp.r[3]+ra[k].x0*hh, tmp.r[4]+ra[k].x0*hh, par);
 		mglStartThread(mgl_qo2d_hprep,0,2*nx,0,0,0,0,&tmp);
 		// Step for field
 		dual dt = dual(0, -ra[k].dt*k0);
 		for(i=0;i<2*nx;i++)		a[i] *= exp(hx[i]*dt);
-		gsl_fft_complex_transform((double *)a, 1, 2*nx, wtx, wsx, forward);
+		mgl_fft((double *)a, 1, 2*nx, wtx, wsx, false);
 		for(i=0;i<2*nx;i++)		a[i] *= exp(hu[i]*dt)/(2.*nx);
-		gsl_fft_complex_transform((double *)a, 1, 2*nx, wtx, wsx, backward);
+		mgl_fft((double *)a, 1, 2*nx, wtx, wsx, true);
 
 /*		// Calculate B1			// TODO make more general scheme later!!!
 		hh = ra[k].pt*(1/sqrt(sqrt(1.041))-1);
-		var['x'-'a'] = ray->a[7*k];	// new coordiantes
-		var['y'-'a'] = ray->a[7*k+1];
-		var['p'-'a'] = ray->a[7*k+3] + ra[k].x0*hh;	// new momentums
-		var['q'-'a'] = ray->a[7*k+4] + ra[k].y0*hh;
+		var['x'-'a'] = ray->a[n7*k];	// new coordiantes
+		var['y'-'a'] = ray->a[n7*k+1];
+		var['p'-'a'] = ray->a[n7*k+3] + ra[k].x0*hh;	// new momentums
+		var['q'-'a'] = ray->a[n7*k+4] + ra[k].y0*hh;
 		tt = h.CalcD(var,'p')*ra[k].x1 + h.CalcD(var,'q')*ra[k].y1;
-		var['x'-'a'] = ray->a[7*k] + ra[k].x1*dr;	// new coordiantes
-		var['y'-'a'] = ray->a[7*k+1] + ra[k].y1*dr;
+		var['x'-'a'] = ray->a[n7*k] + ra[k].x1*dr;	// new coordiantes
+		var['y'-'a'] = ray->a[n7*k+1] + ra[k].y1*dr;
 		hh = 1 - ra[k].t1*dr;	hh = sqrt(sqrt(0.041+hh*hh*hh*hh));
 		hh = (ra[k].ch*ra[k].pt + ra[k].d1*dr)/(hh*ra[k].ch) - ra[k].pt;
-		var['p'-'a'] = ray->a[7*k+3] + ra[k].x0*hh;	// new momentums
-		var['q'-'a'] = ray->a[7*k+4] + ra[k].y0*hh;
+		var['p'-'a'] = ray->a[n7*k+3] + ra[k].x0*hh;	// new momentums
+		var['q'-'a'] = ray->a[n7*k+4] + ra[k].y0*hh;
 		B1 = h.CalcD(var,'p')*ra[k].x1 + h.CalcD(var,'q')*ra[k].y1;
 		B1 = (B1-tt)/dr;
 		double a1=0, a2=0;
@@ -407,10 +391,8 @@ HMDT MGL_EXPORT mgl_qo2d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal px, m
 		a1 = sqrt(a1/a2);
 		for(i=0;i<2*nx;i++)	a[i] *= a1;*/
 	}
-	gsl_fft_complex_workspace_free(wsx);
-	gsl_fft_complex_wavetable_free(wtx);
+	mgl_fft_free(wtx,&wsx,1);
 	delete []a;		delete []hu;	delete []hx;	delete []ra;	delete []dmp;
-#endif
 	return res;
 }
 //-----------------------------------------------------------------------------
@@ -506,15 +488,15 @@ HMDT MGL_EXPORT mgl_qo3d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal z, mr
 	mglData *res=new mglData;
 	const mglData *ray=dynamic_cast<const mglData *>(ray_dat);	// NOTE: Ray must be mglData!
 	if(!ray)	return res;
-	int nx=ini_re->GetNx(), nt=ray->ny;	// NOTE: only square grids are supported now (for simplicity)
+	int nx=ini_re->GetNx(), nt=ray->ny, n7=ray->nx;	// NOTE: only square grids are supported now (for simplicity)
 	if(nx<2 || ini_re->GetNx()!=nx || ini_im->GetNx()*ini_im->GetNy()!=nx*nx || nt<2)	return res;
 	mgl_data_create(res,nx,nx,nt);
-#if MGL_HAVE_GSL
+
 	dual *a=new dual[4*nx*nx], *huv=new dual[4*nx*nx],  *hxy=new dual[4*nx*nx], *huy=new dual[4*nx*nx],  *hxv=new dual[4*nx*nx];
 	dual *hu=new dual[2*nx],  *hx=new dual[2*nx], *hy=new dual[2*nx],  *hv=new dual[2*nx];
 	mreal *dmp=new mreal[4*nx*nx];
 	mgl_ap *ra = new mgl_ap[nt];
-	mgl_init_ra(ray->ny, ray->a, ra);	// prepare ray
+	mgl_init_ra(nt, n7, ray->a, ra);	// prepare ray
 	register int i,j,ii;
 
 	mreal dr = r/(nx-1), dk = M_PI*(nx-1)/(k0*r*nx), tt, x1, x2, hh, B1, pt0;
@@ -529,8 +511,7 @@ HMDT MGL_EXPORT mgl_qo3d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal z, mr
 	}
 	for(i=0;i<nx;i++)	for(j=0;j<nx;j++)	// init
 		a[i+nx/2+2*nx*(j+nx/2)] = dual(ini_re->v(i,j),ini_im->v(i,j));
-	gsl_fft_complex_wavetable *wtx = gsl_fft_complex_wavetable_alloc(2*nx);
-	gsl_fft_complex_workspace *wsx = gsl_fft_complex_workspace_alloc(2*nx);
+	void *wsx, *wtx = mgl_fft_alloc(2*nx,&wsx,1);
 	if(xx && yy && zz)	{	xx->Create(nx,nx,nt);	yy->Create(nx,nx,nt);	zz->Create(nx,nx,nt);	}
 
 	mgl_qo3d_ham tmp;	// parameters for Hamiltonian calculation
@@ -546,11 +527,11 @@ HMDT MGL_EXPORT mgl_qo3d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal z, mr
 		if(xx && yy && zz)	for(i=0;i<nx;i++)	for(j=0;j<nx;j++)	// prepare xx, yy, zz
 		{
 			x1 = (2*i-nx+1)*dr;	x2 = (2*j-nx+1)*dr;
-			xx->a[i+nx*(j+k*nx)] = ray->a[7*k] + ra[k].x1*x1 + ra[k].x2*x2;	// new coordinates
-			yy->a[i+nx*(j+k*nx)] = ray->a[7*k+1] + ra[k].y1*x1 + ra[k].y2*x2;
-			zz->a[i+nx*(j+k*nx)] = ray->a[7*k+2] + ra[k].z1*x1 + ra[k].z2*x2;
+			xx->a[i+nx*(j+k*nx)] = ray->a[n7*k] + ra[k].x1*x1 + ra[k].x2*x2;	// new coordinates
+			yy->a[i+nx*(j+k*nx)] = ray->a[n7*k+1] + ra[k].y1*x1 + ra[k].y2*x2;
+			zz->a[i+nx*(j+k*nx)] = ray->a[n7*k+2] + ra[k].z1*x1 + ra[k].z2*x2;
 		}
-		tmp.r=ray->a+7*k;	tmp.ra=ra+k;
+		tmp.r=ray->a+n7*k;	tmp.ra=ra+k;
 		mglStartThread(mgl_qo3d_hprep,0,2*nx,0,0,0,0,&tmp);	tmp.h0 = huv[0];
 		for(i=0;i<2*nx;i++)	// fill intermediate arrays
 		{
@@ -562,30 +543,30 @@ HMDT MGL_EXPORT mgl_qo3d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal z, mr
 		dual dt = dual(0, -ra[k].dt*k0);	// TODO: this part can be paralleled
 		for(i=0;i<4*nx*nx;i++)	a[i] *= exp(hxy[i]*dt);		// x-y
 		for(i=0;i<2*nx;i++)	// x->u
-			gsl_fft_complex_transform((double *)(a+i*2*nx), 1, 2*nx, wtx, wsx, forward);
+			mgl_fft((double *)(a+i*2*nx), 1, 2*nx, wtx, wsx, false);
 		for(i=0;i<4*nx*nx;i++)	a[i] *= exp(huy[i]*dt);		// u-y
 		for(i=0;i<2*nx;i++)	// y->v
-			gsl_fft_complex_transform((double *)(a+i), 2*nx, 2*nx, wtx, wsx, forward);
+			mgl_fft((double *)(a+i), 2*nx, 2*nx, wtx, wsx, false);
 		for(i=0;i<4*nx*nx;i++)	a[i] *= exp(huv[i]*dt)/(4.*nx*nx);	// u-v
 		for(i=0;i<2*nx;i++)	// u->x
-			gsl_fft_complex_transform((double *)(a+i*2*nx), 1, 2*nx, wtx, wsx, backward);
+			mgl_fft((double *)(a+i*2*nx), 1, 2*nx, wtx, wsx, true);
 		for(i=0;i<4*nx*nx;i++)	a[i] *= exp(hxv[i]*dt);		// x-v
 		for(i=0;i<2*nx;i++)	// v->y
-			gsl_fft_complex_transform((double *)(a+i), 2*nx, 2*nx, wtx, wsx, backward);
+			mgl_fft((double *)(a+i), 2*nx, 2*nx, wtx, wsx, true);
 		
 /*		// Calculate B1			// TODO make more general scheme later!!!
 		hh = ra[k].pt*(1/sqrt(sqrt(1.041))-1);
-		var['x'-'a'] = ray->a[7*k];	// new coordiantes
-		var['y'-'a'] = ray->a[7*k+1];
-		var['p'-'a'] = ray->a[7*k+3] + ra[k].x0*hh;	// new momentums
-		var['q'-'a'] = ray->a[7*k+4] + ra[k].y0*hh;
+		var['x'-'a'] = ray->a[n7*k];	// new coordiantes
+		var['y'-'a'] = ray->a[n7*k+1];
+		var['p'-'a'] = ray->a[n7*k+3] + ra[k].x0*hh;	// new momentums
+		var['q'-'a'] = ray->a[n7*k+4] + ra[k].y0*hh;
 		tt = h.CalcD(var,'p')*ra[k].x1 + h.CalcD(var,'q')*ra[k].y1;
-		var['x'-'a'] = ray->a[7*k] + ra[k].x1*dr;	// new coordiantes
-		var['y'-'a'] = ray->a[7*k+1] + ra[k].y1*dr;
+		var['x'-'a'] = ray->a[n7*k] + ra[k].x1*dr;	// new coordiantes
+		var['y'-'a'] = ray->a[n7*k+1] + ra[k].y1*dr;
 		hh = 1 - ra[k].t1*dr;	hh = sqrt(sqrt(0.041+hh*hh*hh*hh));
 		hh = (ra[k].ch*ra[k].pt + ra[k].d1*dr)/(hh*ra[k].ch) - ra[k].pt;
-		var['p'-'a'] = ray->a[7*k+3] + ra[k].x0*hh;	// new momentums
-		var['q'-'a'] = ray->a[7*k+4] + ra[k].y0*hh;
+		var['p'-'a'] = ray->a[n7*k+3] + ra[k].x0*hh;	// new momentums
+		var['q'-'a'] = ray->a[n7*k+4] + ra[k].y0*hh;
 		B1 = h.CalcD(var,'p')*ra[k].x1 + h.CalcD(var,'q')*ra[k].y1;
 		B1 = (B1-tt)/dr;
 		double a1=0, a2=0;
@@ -596,14 +577,10 @@ HMDT MGL_EXPORT mgl_qo3d_func(dual (*ham)(mreal u, mreal x, mreal y, mreal z, mr
 		a1 = sqrt(a1/a2);
 		for(i=0;i<2*nx;i++)	a[i] *= a1;*/
 	}
-	gsl_fft_complex_workspace_free(wsx);
-	gsl_fft_complex_wavetable_free(wtx);
-	delete []a;
-	delete []ra;
-	delete []dmp;
+	mgl_fft_free(wtx,&wsx,1);
+	delete []a;		delete []ra;	delete []dmp;
 	delete []huv;	delete []hxy;	delete []hxv;	delete []huy;
 	delete []hu;	delete []hx;	delete []hv;	delete []hy;
-#endif
 	return res;
 }
 //-----------------------------------------------------------------------------
diff --git a/src/pixel.cpp b/src/pixel.cpp
index 9e0f136..fb312c2 100644
--- a/src/pixel.cpp
+++ b/src/pixel.cpp
@@ -183,8 +183,10 @@ mglPoint mglCanvas::CalcScr(mglPoint p) const
 {	int x,y;	CalcScr(p,&x,&y);	return mglPoint(x,y);	}
 //-----------------------------------------------------------------------------
 MGL_NO_EXPORT int mgl_type_prior[8]={1,2,4,5, 0,3,0, 7};
+bool mglCreationOrder=false;
 bool operator<(const mglPrim &a, const mglPrim &b)
 {
+	if(mglCreationOrder)	return a.n1<b.n1;
 	register int t1 = mgl_type_prior[a.type], t2 = mgl_type_prior[b.type];
 	if(a.z!=b.z) 	return a.z < b.z;
 	if(t1!=t2)		return t1 > t2;
@@ -194,6 +196,7 @@ bool operator<(const mglPrim &a, const mglPrim &b)
 //-----------------------------------------------------------------------------
 bool operator>(const mglPrim &a, const mglPrim &b)
 {
+	if(mglCreationOrder)	return a.n1>b.n1;
 	register int t1 = mgl_type_prior[a.type], t2 = mgl_type_prior[b.type];
 	if(a.z!=b.z) 	return a.z > b.z;
 	if(t1!=t2)		return t1 < t2;
@@ -280,9 +283,18 @@ void mglCanvas::PreparePrim(bool fast)
 	mglStartThread(&mglCanvas::pxl_transform,this,Pnt.size());
 	if(fast)	mglStartThread(&mglCanvas::pxl_setz,this,Prm.size());
 	else	mglStartThread(&mglCanvas::pxl_setz_adv,this,Prm.size());
+	mglCreationOrder = false;
 	std::sort(Prm.begin(), Prm.end());
 }
 //-----------------------------------------------------------------------------
+void mglBase::resort()
+{
+	mglCreationOrder = true;
+	std::sort(Prm.begin(), Prm.end());
+	mglCreationOrder = false;
+	clr(MGL_FINISHED);
+}
+//-----------------------------------------------------------------------------
 void mglCanvas::pxl_primdr(size_t id, size_t , const void *)
 {
 	int nx=1,ny=1;
@@ -304,11 +316,11 @@ void mglCanvas::pxl_primdr(size_t id, size_t , const void *)
 		d.ObjId = p.id;	d.PenWidth=p.w;
 		switch(p.type)
 		{
-		case 0:	mark_draw(p.n1,p.n4,p.s,&d);	break;
-		case 1:	line_draw(p.n1,p.n2,&d);		break;
-		case 2:	trig_draw(p.n1,p.n2,p.n3,true,&d);	break;
-		case 3:	quad_draw(p.n1,p.n2,p.n3,p.n4,&d);	break;
-		case 4:	glyph_draw(&p,&d);	break;
+		case 0:	mark_draw(Pnt[p.n1],p.n4,p.s,&d);	break;
+		case 1:	line_draw(Pnt[p.n1],Pnt[p.n2],&d);		break;
+		case 2:	trig_draw(Pnt[p.n1],Pnt[p.n2],Pnt[p.n3],true,&d);	break;
+		case 3:	quad_draw(Pnt[p.n1],Pnt[p.n2],Pnt[p.n3],Pnt[p.n4],&d);	break;
+		case 4:	glyph_draw(p,&d);	break;
 		}
 	}
 }
@@ -365,14 +377,16 @@ void mglCanvas::pxl_other(size_t id, size_t n, const void *p)
 	size_t i,j,k;
 	const mglCanvas *gr = (const mglCanvas *)p;
 	if(!gr)	return;
-	for(k=id;k<n;k+=mglNumThr)
+	if(Quality&2)	for(k=id;k<n;k+=mglNumThr)
+	{
+		i = k%Width;	j = Height-1-(k/Width);
+		pnt_plot(i,j,gr->Z[3*k+2],gr->C+12*k+8,gr->OI[k]);
+		pnt_plot(i,j,gr->Z[3*k+1],gr->C+12*k+4,gr->OI[k]);
+		pnt_plot(i,j,gr->Z[3*k],gr->C+12*k,gr->OI[k]);
+	}
+	else	for(k=id;k<n;k+=mglNumThr)
 	{
 		i = k%Width;	j = Height-1-(k/Width);
-		if(Quality&2)
-		{
-			pnt_plot(i,j,gr->Z[3*k+2],gr->C+12*k+8,gr->OI[k]);
-			pnt_plot(i,j,gr->Z[3*k+1],gr->C+12*k+4,gr->OI[k]);
-		}
 		pnt_plot(i,j,gr->Z[3*k],gr->C+12*k,gr->OI[k]);
 	}
 }
@@ -383,49 +397,6 @@ void mglCanvas::Combine(const mglCanvas *gr)
 	mglStartThread(&mglCanvas::pxl_other,this,Width*Height,gr);
 }
 //-----------------------------------------------------------------------------
-#if MGL_HAVE_MPI
-#include <mpi.h>
-#define MCW		MPI_COMM_WORLD
-#define TAG_DATA_Z	0
-#define TAG_DATA_C	1
-void mglCanvas::MPI_Send(int id)
-{
-	Finish();
-	::MPI_Send(Z,3*Width*Height,MPI_FLOAT,id,TAG_DATA_Z,MCW);
-	::MPI_Send(C,12*Width*Height,MPI_CHAR,id,TAG_DATA_C,MCW);
-	::MPI_Send(OI,Width*Height,MPI_INT,id,TAG_DATA_C,MCW);
-}
-void mglCanvas::MPI_Recv(int id)
-{
-	Finish();
-	MPI_Status status;
-	long n = Width*Height;
-	float *zz = new float[3*n];
-	int *oi = new int[n];
-	unsigned char *cc = new unsigned char[12*n];
-	::MPI_Recv(zz,3*n,MPI_FLOAT,id,TAG_DATA_Z,MCW,&status);
-	::MPI_Recv(cc,12*n,MPI_CHAR,id,TAG_DATA_C,MCW,&status);
-	::MPI_Recv(oi,n,MPI_INT,id,TAG_DATA_C,MCW,&status);
-	// TODO check status for errors
-	register long i,j,k;
-	for(k=0;k<n;k++)
-	{	// i0=x+Width*(Height-1-y)
-		i = k%Width;	j = Height-1-(k/Width);
-		if(Quality&2)
-		{
-			pnt_plot(i,j,zz[3*k+2],cc+12*k+8,oi[k]);
-			pnt_plot(i,j,zz[3*k+1],cc+12*k+4,oi[k]);
-		}
-		pnt_plot(i,j,zz[3*k],cc+12*k,oi[k]);
-	}
-	set(MGL_FINISHED);
-	delete []oi; 	delete []zz; 	delete []cc;
-}
-#else
-void mglCanvas::MPI_Send(int /*id*/)	{	mglGlobalMess += "MPI support was disabled. Please, enable it and rebuild MathGL.\n";	}
-void mglCanvas::MPI_Recv(int /*id*/)	{	mglGlobalMess += "MPI support was disabled. Please, enable it and rebuild MathGL.\n";	}
-#endif
-//-----------------------------------------------------------------------------
 void mglCanvas::pnt_plot(long x,long y,mreal z,const unsigned char ci[4], int obj_id)
 {
 	long i0=x+Width*(Height-1-y);
@@ -586,17 +557,16 @@ unsigned char **mglCanvas::GetRGBLines(long &w, long &h, unsigned char *&f, bool
 /* Bilinear interpolation r(u,v) = r0 + (r1-r0)*u + (r2-r0)*v + (r3+r0-r1-r2)*u*v
 	is used (where r is one of {x,y,z,R,G,B,A}. Variables u,v are determined
 	for each point (x,y) and selected one pair which 0<u<1 and 0<v<1.*/
-void mglCanvas::quad_draw(long k1, long k2, long k3, long k4, mglDrawReg *d)
+void mglCanvas::quad_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, const mglPnt &p4, mglDrawReg *d)
 {
 	if(!(Quality&3))
 	{
-		fast_draw(k1,k2,d);	fast_draw(k1,k3,d);
-		fast_draw(k4,k2,d);	fast_draw(k4,k3,d);	return;
+		fast_draw(p1,p2,d);	fast_draw(p1,p3,d);
+		fast_draw(p4,p2,d);	fast_draw(p4,p3,d);	return;
 	}
 	unsigned char r[4];
 	long y1,x1,y2,x2;
 	float dd,dsx,dsy;
-	const mglPnt &p1=Pnt[k1], &p2=Pnt[k2], &p3=Pnt[k3], &p4=Pnt[k4];
 	mglPnt d1=p2-p1, d2=p3-p1, d3=p4+p1-p2-p3, p;
 
 	x1 = long(fmin(fmin(p1.x,p2.x),fmin(p3.x,p4.x)));	// bounding box
@@ -612,7 +582,7 @@ void mglCanvas::quad_draw(long k1, long k2, long k3, long k4, mglDrawReg *d)
 	dsy = 4*(d2.y*d3.x - d2.x*d3.y)*d1.x;
 
 	if((d1.x==0 && d1.y==0) || (d2.x==0 && d2.y==0) || !(Quality&2))
-	{	trig_draw(k1,k2,k4,true,d);	trig_draw(k1,k3,k4,true,d);	return;	}
+	{	trig_draw(p1,p2,p4,true,d);	trig_draw(p1,p3,p4,true,d);	return;	}
 
 	mglPoint n1 = mglPoint(p2.x-p1.x,p2.y-p1.y,p2.z-p1.z)^mglPoint(p3.x-p1.x,p3.y-p1.y,p3.z-p1.z);
 	mglPoint n2 = mglPoint(p2.x-p4.x,p2.y-p4.y,p2.z-p4.z)^mglPoint(p3.x-p4.x,p3.y-p4.y,p3.z-p4.z);
@@ -657,17 +627,16 @@ void mglCanvas::quad_draw(long k1, long k2, long k3, long k4, mglDrawReg *d)
 /* Linear interpolation r(u,v) = r0 + (r1-r0)*u + (r2-r0)*v is used, where r is
 	one of {x,y,z,R,G,B,A}. Variables u,v are determined for each point (x,y).
 	Point plotted is u>0 and v>0 and u+v<1.*/
-void mglCanvas::trig_draw(long k1, long k2, long k3, bool anorm, mglDrawReg *d)
+void mglCanvas::trig_draw(const mglPnt &p1, const mglPnt &p2, const mglPnt &p3, bool anorm, mglDrawReg *d)
 {
-	if(!(Quality&3))
+	if(!(Quality&3) && anorm)
 	{
-		fast_draw(k1,k2,d);	fast_draw(k1,k3,d);
-		fast_draw(k2,k3,d);	return;
+		fast_draw(p1,p2,d);	fast_draw(p1,p3,d);
+		fast_draw(p2,p3,d);	return;
 	}
 	unsigned char r[4];
 	long y1,x1,y2,x2;
 	float dxu,dxv,dyu,dyv;
-	const mglPnt &p1=Pnt[k1], &p2=Pnt[k2], &p3=Pnt[k3];
 	mglPnt d1=p2-p1, d2=p3-p1, p;
 
 	dxu = d2.x*d1.y - d1.x*d2.y;
@@ -688,26 +657,28 @@ void mglCanvas::trig_draw(long k1, long k2, long k3, bool anorm, mglDrawReg *d)
 	register float u,v,xx,yy;
 	register long i,j;
 	float x0 = p1.x, y0 = p1.y;
-	for(i=x1;i<=x2;i++)	for(j=y1;j<=y2;j++)
+	if(Quality&2)	for(i=x1;i<=x2;i++)	for(j=y1;j<=y2;j++)
 	{
 		xx = (i-x0);	yy = (j-y0);
 		u = dxu*xx+dyu*yy;	v = dxv*xx+dyv*yy;
 		if(u<0 || v<0 || u+v>1)	continue;
-		if(Quality&2)	// slow but accurate
-		{
-			p = p1+d1*u+d2*v;
-			if(mgl_isnan(p.u) && !mgl_isnan(p.v) && anorm)
-			{	p.u = nr.x;	p.v = nr.y;	p.w = nr.z;	}
-			pnt_plot(i,j,p.z,col2int(p,r,d->ObjId),d->ObjId);
-		}
-		else 	pnt_plot(i,j,p1.z,col2int(p1,r,d->ObjId),d->ObjId);
+		p = p1+d1*u+d2*v;
+		if(mgl_isnan(p.u) && !mgl_isnan(p.v) && anorm)
+		{	p.u = nr.x;	p.v = nr.y;	p.w = nr.z;	}
+		pnt_plot(i,j,p.z,col2int(p,r,d->ObjId),d->ObjId);
+	}
+	else	for(i=x1;i<=x2;i++)	for(j=y1;j<=y2;j++)
+	{
+		xx = (i-x0);	yy = (j-y0);
+		u = dxu*xx+dyu*yy;	v = dxv*xx+dyv*yy;
+		if(u<0 || v<0 || u+v>1)	continue;
+		pnt_plot(i,j,p1.z,col2int(p1,r,d->ObjId),d->ObjId);
 	}
 }
 //-----------------------------------------------------------------------------
-void mglCanvas::line_draw(long k1, long k2, mglDrawReg *dr)
+void mglCanvas::line_draw(const mglPnt &p1, const mglPnt &p2, mglDrawReg *dr)
 {
-	if(k1>k2)	{	long kk=k1;	k1=k2;	k2=kk;	}	// rearrange start/end for proper dashing
-	if(!(Quality&3))	{	fast_draw(k1,k2,dr);	return;	}
+	if(!(Quality&3))	{	fast_draw(p1,p2,dr);	return;	}
 	unsigned char r[4];
 	long y1,x1,y2,x2;
 
@@ -715,7 +686,6 @@ void mglCanvas::line_draw(long k1, long k2, mglDrawReg *dr)
 	float dz = Width>2 ? 1 : 1e-5*Width;		// provide additional height to be well visible on the surfaces
 
 	if(dr->ObjId==HighId)	{	pw *= 2;	dpw=2;	}
-	const mglPnt &p1=Pnt[k1], &p2=Pnt[k2];
 	mglPnt d=p2-p1, p;
 	bool hor = fabs(d.x)>fabs(d.y);
 
@@ -777,9 +747,8 @@ void mglCanvas::line_draw(long k1, long k2, mglDrawReg *dr)
 	set(aa,MGL_ENABLE_ALPHA);
 }
 //-----------------------------------------------------------------------------
-void mglCanvas::fast_draw(long k1, long k2, mglDrawReg *dr)
+void mglCanvas::fast_draw(const mglPnt &p1, const mglPnt &p2, mglDrawReg *dr)
 {
-	const mglPnt &p1=Pnt[k1], &p2=Pnt[k2];
 	mglPnt d=p2-p1;
 	unsigned char r[4];	col2int(p1,r,dr->ObjId);
 	long y1,x1,y2,x2;
@@ -807,43 +776,43 @@ void mglCanvas::fast_draw(long k1, long k2, mglDrawReg *dr)
 	}
 }
 //-----------------------------------------------------------------------------
-void mglCanvas::pnt_draw(long k, mglDrawReg *dr)
+void mglCanvas::pnt_draw(const mglPnt &p, mglDrawReg *dr)
 {
-	register long i,j,s,x,y;
+//	if(k<0 || !dr)	return;
+	register long i,j;
 	register float v,pw=3*dr->PenWidth,dpw=3;
 	if(dr->ObjId==HighId)	{	pw *= 2;	dpw=2;	}
-	const mglPnt &p=Pnt[k];
 	unsigned char cs[4], cc;
 	col2int(p,cs,dr->ObjId);	cc = cs[3];
 	if(cc==0)	return;
-	s = long(5.5+fabs(pw));
-	for(j=-s;j<=s;j++)	for(i=-s;i<=s;i++)
+	long s = long(5.5+fabs(pw));
+	long i1=fmax(-s,dr->x1-p.x),i2=fmin(s,dr->x2-p.x), j1=fmax(-s,dr->y1-p.y),j2=fmin(s,dr->y2-p.y);
+	if(!(Quality&3))	for(j=j1;j<=j2;j++)	for(i=i1;i<=i2;i++)	// fast draw
+	{
+		v = i*i+j*j;
+		if(v>1+(pw-1)*(pw-1)/4)	continue;
+		pnt_plot(p.x+i,p.y+j,p.z,cs,dr->ObjId);
+	}
+	else	for(j=j1;j<=j2;j++)	for(i=i1;i<=i2;i++)
 	{
 		v = i*i+j*j;
 		cs[3] = v<(pw-1)*(pw-1)/4 ? cc : (unsigned char)(cc/cosh(dpw*(sqrt(v)+(1-pw)/2)));
 //		cs[3] = (unsigned char)(cc*exp(-6*v));
-		if(cs[3]==0)	continue;
-		x=p.x+i;	y=p.y+j;
-		if(x>=dr->x1 && x<=dr->x2 && y>=dr->y1 && y<=dr->y2)
-			pnt_plot(p.x+i,p.y+j,p.z,cs,dr->ObjId);
+		pnt_plot(p.x+i,p.y+j,p.z,cs,dr->ObjId);
 	}
 }
 //-----------------------------------------------------------------------------
-void mglCanvas::mark_draw(long k, char type, mreal size, mglDrawReg *d)
+void mglCanvas::mark_draw(const mglPnt &q, char type, mreal size, mglDrawReg *d)
 {
-	const mglPnt &q=Pnt[k];
 	unsigned char cs[4];	col2int(q,cs,d->ObjId);	cs[3] = size>0 ? 255 : 255*q.t;
-	mglPnt p=q;
+	mglPnt p0=q,p1=q,p2=q,p3=q;
 	mreal ss=fabs(size);
 	register long i,j;
-#if MGL_HAVE_PTHREAD
-	pthread_mutex_lock(&mutexPnt);
-#endif
-	size_t pos = Pnt.size(), qos=pos;
+
 	if(type=='.' || ss==0)
 	{
 		if(d)	d->PenWidth = ss?ss:sqrt(font_factor/400);
-		pnt_draw(k,d);
+		pnt_draw(q,d);
 	}
 	else
 	{
@@ -856,107 +825,100 @@ void mglCanvas::mark_draw(long k, char type, mreal size, mglDrawReg *d)
 		switch(type)
 		{
 		case 'P':
-			p.x = q.x-ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y-ss;	Pnt.push_back(p);	line_draw(pos,pos+1,d);
-			p.x = q.x+ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(pos+1,pos+2,d);
-			p.x = q.x-ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(pos+2,pos+3,d);
-			line_draw(pos+3,pos,d);	qos+=4;
+			p0.x = q.x-ss;	p0.y = q.y-ss;	p1.x = q.x+ss;	p1.y = q.y-ss;
+			p2.x = q.x+ss;	p2.y = q.y+ss;	p3.x = q.x-ss;	p3.y = q.y+ss;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p3,d);	line_draw(p3,p0,d);
 		case '+':
-			p.x = q.x-ss;	p.y = q.y;		Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y;		Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x = q.x;	p.y = q.y-ss;		Pnt.push_back(p);
-			p.x = q.x;	p.y = q.y+ss;		Pnt.push_back(p);	line_draw(qos+2,qos+3,d);
+			p0.x = q.x-ss;	p0.y = q.y;	p1.x = q.x+ss;	p1.y = q.y;	line_draw(p0,p1,d);
+			p2.x = q.x;	p2.y = q.y-ss;	p3.x = q.x;	p3.y = q.y+ss;	line_draw(p2,p3,d);
 			break;
 		case 'X':
-			p.x = q.x-ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y-ss;	Pnt.push_back(p);	line_draw(pos,pos+1,d);
-			p.x = q.x+ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(pos+1,pos+2,d);
-			p.x = q.x-ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(pos+2,pos+3,d);
-			line_draw(pos+3,pos,d);	qos+=4;
+			p0.x = q.x-ss;	p0.y = q.y-ss;	p1.x = q.x+ss;	p1.y = q.y-ss;
+			p2.x = q.x+ss;	p2.y = q.y+ss;	p3.x = q.x-ss;	p3.y = q.y+ss;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p3,d);	line_draw(p3,p0,d);
 		case 'x':
-			p.x = q.x-ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x = q.x+ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x = q.x-ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(qos+2,qos+3,d);
+			p0.x = q.x-ss;	p0.y = q.y-ss;	p1.x = q.x+ss;	p1.y = q.y+ss;	line_draw(p0,p1,d);
+			p2.x = q.x+ss;	p2.y = q.y-ss;	p3.x = q.x-ss;	p3.y = q.y+ss;	line_draw(p2,p3,d);
 			break;
 		case 'S':
-			p.x = q.x-ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x = q.x-ss;	p.y = q.y+ss;	Pnt.push_back(p);
-			p.x= q.x+ss;	p.y= q.y+ss;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			quad_draw(pos,pos+1,pos+3,pos+2,d);	qos+=4;
+			p0.x = q.x-ss;	p0.y = q.y-ss;	p1.x = q.x-ss;	p1.y = q.y+ss;
+			p2.x= q.x+ss;	p2.y= q.y+ss;	p3.x = q.x+ss;	p3.y = q.y-ss;
+			quad_draw(p0,p1,p3,p2,d);
 		case 's':
-			p.x = q.x-ss;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y-ss;	Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x = q.x+ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(qos+1,qos+2,d);
-			p.x = q.x-ss;	p.y = q.y+ss;	Pnt.push_back(p);	line_draw(qos+2,qos+3,d);
-			line_draw(qos+3,qos,d);	break;
+			p0.x = q.x-ss;	p0.y = q.y-ss;	p1.x = q.x+ss;	p1.y = q.y-ss;
+			p2.x = q.x+ss;	p2.y = q.y+ss;	p3.x = q.x-ss;	p3.y = q.y+ss;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p3,d);	line_draw(p3,p0,d);
+			break;
 		case 'D':
-			p.x = q.x;	p.y = q.y-ss;		Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y;		Pnt.push_back(p);
-			p.x= q.x;	p.y= q.y+ss;		Pnt.push_back(p);
-			p.x = q.x-ss;	p.y = q.y;		Pnt.push_back(p);
-			quad_draw(pos,pos+1,pos+3,pos+2,d);	qos+=4;
+			p0.x = q.x;	p0.y = q.y-ss;	p1.x = q.x+ss;	p1.y = q.y;
+			p2.x= q.x;	p2.y= q.y+ss;	p3.x = q.x-ss;	p3.y = q.y;
+			quad_draw(p0,p1,p3,p2,d);
 		case 'd':
-			p.x = q.x;	p.y = q.y-ss;		Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y;		Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x = q.x;	p.y = q.y+ss;		Pnt.push_back(p);	line_draw(qos+1,qos+2,d);
-			p.x = q.x-ss;	p.y = q.y;		Pnt.push_back(p);	line_draw(qos+2,qos+3,d);
-			line_draw(qos+3,qos,d);	break;
+			p0.x = q.x;	p0.y = q.y-ss;	p1.x = q.x+ss;	p1.y = q.y;
+			p2.x = q.x;	p2.y = q.y+ss;	p3.x = q.x-ss;	p3.y = q.y;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p3,d);	line_draw(p3,p0,d);
+			break;
 		case 'Y':
-			p.x = q.x;	p.y = q.y;			Pnt.push_back(p);
-			p.x = q.x;	p.y = q.y-ss;		Pnt.push_back(p);	line_draw(pos,pos+1,d);
-			p.x = q.x-0.8*ss;	p.y = q.y+0.6*ss;	Pnt.push_back(p);	line_draw(pos,pos+2,d);
-			p.x = q.x+0.8*ss;	p.y = q.y+0.6*ss;	Pnt.push_back(p);	line_draw(pos,pos+3,d);
+			p1.x = q.x;	p1.y = q.y-ss;	line_draw(q,p1,d);
+			p2.x = q.x-0.8*ss;	p2.y = q.y+0.6*ss;	line_draw(q,p2,d);
+			p3.x = q.x+0.8*ss;	p3.y = q.y+0.6*ss;	line_draw(q,p3,d);
 			break;
 		case '*':
-			p.x = q.x-ss;		p.y = q.y;	Pnt.push_back(p);
-			p.x = q.x+ss;		p.y = q.y;	Pnt.push_back(p);	line_draw(pos,pos+1,d);
-			p.x = q.x-0.6*ss;	p.y = q.y-0.8*ss;	Pnt.push_back(p);
-			p.x = q.x+0.6*ss;	p.y = q.y+0.8*ss;	Pnt.push_back(p);	line_draw(pos+2,pos+3,d);
-			p.x = q.x-0.6*ss;	p.y = q.y+0.8*ss;	Pnt.push_back(p);
-			p.x = q.x+0.6*ss;	p.y = q.y-0.8*ss;	Pnt.push_back(p);	line_draw(pos+4,pos+5,d);
+			p0.x = q.x-ss;		p0.y = q.y;
+			p1.x = q.x+ss;		p1.y = q.y;	line_draw(p0,p1,d);
+			p0.x = q.x-0.6*ss;	p0.y = q.y-0.8*ss;
+			p1.x = q.x+0.6*ss;	p1.y = q.y+0.8*ss;	line_draw(p0,p1,d);
+			p0.x = q.x-0.6*ss;	p0.y = q.y+0.8*ss;
+			p1.x = q.x+0.6*ss;	p1.y = q.y-0.8*ss;	line_draw(p0,p1,d);
 			break;
 		case 'T':
-			p.x = q.x-ss;	p.y = q.y-ss/2;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y-ss/2;	Pnt.push_back(p);
-			p.x= q.x;		p.y= q.y+ss;	Pnt.push_back(p);
-			trig_draw(pos,pos+1,pos+2,false,d);	qos+=3;
+			p0.x = q.x-ss;	p0.y = q.y-ss/2;
+			p1.x = q.x+ss;	p1.y = q.y-ss/2;
+			p2.x= q.x;		p2.y= q.y+ss;
+			trig_draw(p0,p1,p2,false,d);
 		case '^':
-			p.x = q.x-ss;	p.y = q.y-ss/2;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y-ss/2;	Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x= q.x;		p.y= q.y+ss;	Pnt.push_back(p);	line_draw(qos+1,qos+2,d);
-			line_draw(qos+2,qos,d);		break;
+			p0.x = q.x-ss;	p0.y = q.y-ss/2;
+			p1.x = q.x+ss;	p1.y = q.y-ss/2;
+			p2.x= q.x;		p2.y= q.y+ss;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p0,d);	break;
 		case 'V':
-			p.x = q.x-ss;	p.y = q.y+ss/2;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y+ss/2;	Pnt.push_back(p);
-			p.x= q.x;		p.y= q.y-ss;	Pnt.push_back(p);
-			trig_draw(pos,pos+1,pos+2,false,d);	qos+=3;
+			p0.x = q.x-ss;	p0.y = q.y+ss/2;
+			p1.x = q.x+ss;	p1.y = q.y+ss/2;
+			p2.x= q.x;		p2.y= q.y-ss;
+			trig_draw(p0,p1,p2,false,d);
 		case 'v':
-			p.x = q.x-ss;	p.y = q.y+ss/2;	Pnt.push_back(p);
-			p.x = q.x+ss;	p.y = q.y+ss/2;	Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x= q.x;		p.y= q.y-ss;	Pnt.push_back(p);	line_draw(qos+1,qos+2,d);
-			line_draw(qos+2,qos,d);		break;
+			p0.x = q.x-ss;	p0.y = q.y+ss/2;
+			p1.x = q.x+ss;	p1.y = q.y+ss/2;
+			p2.x= q.x;		p2.y= q.y-ss;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p0,d);	break;
 		case 'L':
-			p.x = q.x+ss/2;	p.y = q.y+ss;	Pnt.push_back(p);
-			p.x = q.x+ss/2;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x= q.x-ss;	p.y= q.y;		Pnt.push_back(p);
-			trig_draw(pos,pos+1,pos+2,false,d);	qos+=3;
+			p0.x = q.x+ss/2;	p0.y = q.y+ss;
+			p1.x = q.x+ss/2;	p1.y = q.y-ss;
+			p2.x= q.x-ss;		p2.y= q.y;
+			trig_draw(p0,p1,p2,false,d);
 		case '<':
-			p.x = q.x+ss/2;	p.y = q.y+ss;	Pnt.push_back(p);
-			p.x = q.x+ss/2;	p.y = q.y-ss;	Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x= q.x-ss;	p.y= q.y;		Pnt.push_back(p);	line_draw(qos+1,qos+2,d);
-			line_draw(qos+2,qos,d);		break;
+			p0.x = q.x+ss/2;	p0.y = q.y+ss;
+			p1.x = q.x+ss/2;	p1.y = q.y-ss;
+			p2.x= q.x-ss;		p2.y= q.y;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p0,d);	break;
 		case 'R':
-			p.x = q.x-ss/2;	p.y = q.y+ss;	Pnt.push_back(p);
-			p.x = q.x-ss/2;	p.y = q.y-ss;	Pnt.push_back(p);
-			p.x= q.x+ss;	p.y= q.y;		Pnt.push_back(p);
-			trig_draw(pos,pos+1,pos+2,false,d);	qos+=3;
+			p0.x = q.x-ss/2;	p0.y = q.y+ss;
+			p1.x = q.x-ss/2;	p1.y = q.y-ss;
+			p2.x= q.x+ss;		p2.y= q.y;
+			trig_draw(p0,p1,p2,false,d);
 		case '>':
-			p.x = q.x-ss/2;	p.y = q.y+ss;	Pnt.push_back(p);
-			p.x = q.x-ss/2;	p.y = q.y-ss;	Pnt.push_back(p);	line_draw(qos,qos+1,d);
-			p.x= q.x+ss;	p.y= q.y;		Pnt.push_back(p);	line_draw(qos+1,qos+2,d);
-			line_draw(qos+2,qos,d);		break;
+			p0.x = q.x-ss/2;	p0.y = q.y+ss;
+			p1.x = q.x-ss/2;	p1.y = q.y-ss;
+			p2.x= q.x+ss;		p2.y= q.y;
+			line_draw(p0,p1,d);	line_draw(p1,p2,d);
+			line_draw(p2,p0,d);	break;
 		case 'O':
 			for(j=long(-ss);j<=long(ss);j++)	for(i=long(-ss);i<=long(ss);i++)
 			{
@@ -967,24 +929,20 @@ void mglCanvas::mark_draw(long k, char type, mreal size, mglDrawReg *d)
 		case 'o':
 			for(i=0;i<=20;i++)
 			{
-				p.x = q.x+ss*cos(i*M_PI/10);	p.y = q.y+ss*sin(i*M_PI/10);	Pnt.push_back(p);
-				if(i>0)	line_draw(pos+i-1,pos+i,d);
+				p0 = p1;	p1.x = q.x+ss*cos(i*M_PI/10);	p1.y = q.y+ss*sin(i*M_PI/10);
+				if(i>0)	line_draw(p0,p1,d);
 			}
 			break;
 		case 'C':
-			pnt_draw(k,d);
+			pnt_draw(q,d);
 			for(i=0;i<=20;i++)
 			{
-				p.x = q.x+ss*cos(i*M_PI/10);	p.y = q.y+ss*sin(i*M_PI/10);	Pnt.push_back(p);
-				if(i>0)	line_draw(pos+i-1,pos+i,d);
+				p0 = p1;	p1.x = q.x+ss*cos(i*M_PI/10);	p1.y = q.y+ss*sin(i*M_PI/10);
+				if(i>0)	line_draw(p0,p1,d);
 			}
 			break;
 		}
-		Pnt.erase(Pnt.begin()+pos,Pnt.end());
 	}
-#if MGL_HAVE_PTHREAD
-	pthread_mutex_unlock(&mutexPnt);
-#endif
 }
 //-----------------------------------------------------------------------------
 // scale direction for new view/zoom
@@ -1009,31 +967,31 @@ float mglCanvas::GetGlyphPhi(const mglPnt &q, float phi)
 	return phi;
 }
 //-----------------------------------------------------------------------------
-void mglCanvas::glyph_draw(const mglPrim *P, mglDrawReg *d)
+void mglCanvas::glyph_draw(const mglPrim &P, mglDrawReg *d)
 {
-	float phi = GetGlyphPhi(Pnt[P->n2],P->w);
+	float phi = GetGlyphPhi(Pnt[P.n2],P.w);
 	if(mgl_isnan(phi))	return;
 
-	mglPnt p=Pnt[P->n1];
-	mreal pf=sqrt((Bp.b[0]*Bp.b[0]+Bp.b[1]*Bp.b[1]+Bp.b[3]*Bp.b[3]+Bp.b[4]*Bp.b[4])/2), f = P->p*pf;
+	mglPnt p=Pnt[P.n1];
+	mreal pf=sqrt((Bp.b[0]*Bp.b[0]+Bp.b[1]*Bp.b[1]+Bp.b[3]*Bp.b[3]+Bp.b[4]*Bp.b[4])/2), f = P.p*pf;
 #if MGL_HAVE_PTHREAD
 	pthread_mutex_lock(&mutexPnt);
 #endif
 	Push();		B.clear();
-	B.b[0] = B.b[4] = B.b[8] = P->s;
+	B.b[0] = B.b[4] = B.b[8] = P.s;
 	RotateN(phi,0,0,1);
 	B.x=p.x;	B.y=p.y;	B.z=p.z;	B.pf = 1;
 	p.u *= pf;	p.v *= pf;
 
-	const mglGlyph &g = Glf[P->n4];
-	if(P->n3&8)
+	const mglGlyph &g = Glf[P.n4];
+	if(P.n3&8)
 	{
-		if(!(P->n3&4))	glyph_line(p,f,true, d);
+		if(!(P.n3&4))	glyph_line(p,f,true, d);
 		glyph_line(p,f,false, d);
 	}
 	else
 	{
-		if(!(P->n3&4))	glyph_fill(p,f,g, d);
+		if(!(P.n3&4))	glyph_fill(p,f,g, d);
 		glyph_wire(p,f,g, d);
 	}
 	Pop();
@@ -1045,27 +1003,27 @@ void mglCanvas::glyph_draw(const mglPrim *P, mglDrawReg *d)
 void mglCanvas::glyph_fill(const mglPnt &pp, mreal f, const mglGlyph &g, mglDrawReg *d)
 {
 	if(!g.trig || g.nt<=0)	return;
-	long ik,ii,pos=Pnt.size();
-	mglPnt p=pp;	p.u=p.v=NAN;
+	long ik,ii;
+	mglPnt q0=pp, q1=pp, q2=pp;
+	q0.u=q0.v=q1.u=q1.v=q2.u=q2.v=NAN;
 	mglPoint p1,p2,p3;
 	for(ik=0;ik<g.nt;ik++)
 	{
 		ii = 6*ik;	p1 = mglPoint(f*g.trig[ii]+pp.u,f*g.trig[ii+1]+pp.v,0);	PostScale(p1);
 		ii+=2;		p2 = mglPoint(f*g.trig[ii]+pp.u,f*g.trig[ii+1]+pp.v,0);	PostScale(p2);
 		ii+=2;		p3 = mglPoint(f*g.trig[ii]+pp.u,f*g.trig[ii+1]+pp.v,0);	PostScale(p3);
-		p.x = p1.x;	p.y = p1.y;	p.z = p1.z;	Pnt.push_back(p);
-		p.x = p2.x;	p.y = p2.y;	p.z = p2.z;	Pnt.push_back(p);
-		p.x = p3.x;	p.y = p3.y;	p.z = p3.z;	Pnt.push_back(p);
-		ii = Pnt.size()-3;	trig_draw(ii,ii+1,ii+2,false,d);
+		q0.x = p1.x;	q0.y = p1.y;	q0.z = p1.z;
+		q1.x = p2.x;	q1.y = p2.y;	q1.z = p2.z;
+		q2.x = p3.x;	q2.y = p3.y;	q2.z = p3.z;
+		trig_draw(q0,q1,q2,false,d);
 	}
-	Pnt.erase(Pnt.begin()+pos,Pnt.end());
 }
 //-----------------------------------------------------------------------------
 void mglCanvas::glyph_wire(const mglPnt &pp, mreal f, const mglGlyph &g, mglDrawReg *d)
 {
 	if(!g.line || g.nl<=0)	return;
-	long ik,ii,il=0,pos=Pnt.size();
-	mglPnt p=pp;	p.u=p.v=NAN;
+	long ik,ii,il=0;
+	mglPnt q0=pp, q1=pp;	q0.u=q0.v=q1.u=q1.v=NAN;
 	if(d)	{	d->PDef = 0xffff;	d->PenWidth=0.75;	}
 	mglPoint p1,p2;
 	for(ik=0;ik<g.nl;ik++)
@@ -1084,19 +1042,18 @@ void mglCanvas::glyph_wire(const mglPnt &pp, mreal f, const mglGlyph &g, mglDraw
 			p2 = mglPoint(f*g.line[ii]+pp.u,f*g.line[ii+1]+pp.v,0);
 		}
 		PostScale(p1);	PostScale(p2);
-		p.x = p1.x;	p.y = p1.y;	p.z = p1.z;	Pnt.push_back(p);
-		p.x = p2.x;	p.y = p2.y;	p.z = p2.z;	Pnt.push_back(p);
-		ii = Pnt.size()-2;	line_draw(ii,ii+1,d);
+		q0.x = p1.x;	q0.y = p1.y;	q0.z = p1.z;
+		q1.x = p2.x;	q1.y = p2.y;	q1.z = p2.z;
+		line_draw(q0,q1,d);
 	}
-	Pnt.erase(Pnt.begin()+pos,Pnt.end());
 }
 //-----------------------------------------------------------------------------
 void mglCanvas::glyph_line(const mglPnt &pp, mreal f, bool solid, mglDrawReg *d)
 {
-	mglPnt p=pp;	p.u=p.v=NAN;
+	mglPnt q0=pp,q1=pp,q2=pp,q3=pp;
+	q0.u=q0.v=q1.u=q1.v=q2.u=q2.v=q3.u=q3.v=NAN;
 	if(d)	{	d->PDef = 0xffff;	d->PenWidth=1;	}
 	mglPoint p1,p2,p3,p4;
-	long pos=Pnt.size();
 
 	mreal dy = 0.004;
 	p1 = mglPoint(pp.u,pp.v-dy,0);	PostScale(p1);
@@ -1104,18 +1061,17 @@ void mglCanvas::glyph_line(const mglPnt &pp, mreal f, bool solid, mglDrawReg *d)
 	p3 = mglPoint(fabs(f)+pp.u,pp.v+dy,0);	PostScale(p3);
 	p4 = mglPoint(fabs(f)+pp.u,pp.v-dy,0);	PostScale(p4);
 
-	p.x = p1.x;	p.y = p1.y;	p.z = p1.z;	Pnt.push_back(p);
-	p.x = p2.x;	p.y = p2.y;	p.z = p2.z;	Pnt.push_back(p);
-	p.x = p3.x;	p.y = p3.y;	p.z = p3.z;	Pnt.push_back(p);
-	p.x = p4.x;	p.y = p4.y;	p.z = p4.z;	Pnt.push_back(p);
+	q0.x = p1.x;	q0.y = p1.y;	q0.z = p1.z;
+	q1.x = p2.x;	q1.y = p2.y;	q1.z = p2.z;
+	q2.x = p3.x;	q2.y = p3.y;	q2.z = p3.z;
+	q3.x = p4.x;	q3.y = p4.y;	q3.z = p4.z;
 
-	if(solid)	quad_draw(pos,pos+1,pos+3,pos+2,d);
+	if(solid)	quad_draw(q0,q1,q3,q2,d);
 	else
 	{
-		line_draw(pos,pos+1,d);	line_draw(pos+2,pos+1,d);
-		line_draw(pos,pos+3,d);	line_draw(pos+2,pos+3,d);
+		line_draw(q0,q1,d);	line_draw(q2,q1,d);
+		line_draw(q0,q3,d);	line_draw(q2,q3,d);
 	}
-	Pnt.erase(Pnt.begin()+pos,Pnt.end());
 }
 //-----------------------------------------------------------------------------
 long mglCanvas::setPp(mglPnt &q, const mglPoint &p)
@@ -1127,7 +1083,6 @@ long mglCanvas::setPp(mglPnt &q, const mglPoint &p)
 //-----------------------------------------------------------------------------
 void mglCanvas::arrow_draw(long n1, long n2, char st, float ll)
 {
-	if(n1<0 || n2<0 || !strchr("AVKSDTIO",st))	return;
 	const mglPnt &p1=Pnt[n1], &p2=Pnt[n2];
 	mglPnt q=p1; 	//q.u=q.v=q.w=0;
 	
@@ -1178,7 +1133,6 @@ void mglCanvas::arrow_draw(long n1, long n2, char st, float ll)
 //-----------------------------------------------------------------------------
 void mglCanvas::arrow_plot_3d(long n1, long n2, char st, float ll)
 {
-	if(n1<0 || n2<0 || !strchr("AVKSDTIO",st))	return;
 	const mglPnt &p1=Pnt[n1], &p2=Pnt[n2];
 	mglPnt q=p1; 	//q.u=q.v=q.w=0;
 	
diff --git a/src/plot.cpp b/src/plot.cpp
index cb0d855..9eb448d 100644
--- a/src/plot.cpp
+++ b/src/plot.cpp
@@ -1362,10 +1362,7 @@ void face_plot(mglBase *gr, mglPoint o, mglPoint d1, mglPoint d2, mreal c, bool
 	long *id=new long[n*n];
 	gr->Reserve(n*n);
 	for(j=0;j<n;j++)	for(i=0;i<n;i++)
-	{	p = o+d1*i+d2*j;	id[i+n*j] = gr->AddPnt(p,c,nn);
-		if(id[i+n*j]<0)
-		{	printf("q");	gr->AddPnt(p,c,nn);	}
-	}
+	{	p = o+d1*i+d2*j;	id[i+n*j] = gr->AddPnt(p,c,nn);	}
 	for(i=0;i<num;i++)	for(j=0;j<num;j++)
 	{
 		if(gr->Stop)	{	delete []id;	return;	}
diff --git a/texinfo/core_en.texi b/texinfo/core_en.texi
index 8d27592..348a960 100644
--- a/texinfo/core_en.texi
+++ b/texinfo/core_en.texi
@@ -559,8 +559,8 @@ Sets the range for @samp{x}-, at samp{y}-, at samp{z}- coordinate or coloring (@samp{c
 @deftypefn {MGL command} {} ranges @code{x1 x2 y1 y2 [z1=0 z2=0]}
 @ifclear UDAV
 @deftypefnx {Method on @code{mglGraph}} @code{void} SetRanges (@code{mglPoint} p1, @code{mglPoint} p2)
- at deftypefnx {Method on @code{mglGraph}} @code{void} SetRanges (@code{mreal} x1, @code{mreal} x2, @code{mreal} y1, @code{mreal} y2, @code{mreal} z1=@code{0}, @code{mreal} z2=@code{0})
- at deftypefnx {C function} @code{void} mgl_set_ranges (@code{HMGL} gr, @code{mreal} x1, @code{mreal} x2, @code{mreal} y1, @code{mreal} y2, @code{mreal} z1, @code{mreal} z2)
+ at deftypefnx {Method on @code{mglGraph}} @code{void} SetRanges (@code{double} x1, @code{double} x2, @code{double} y1, @code{double} y2, @code{double} z1=@code{0}, @code{double} z2=@code{0})
+ at deftypefnx {C function} @code{void} mgl_set_ranges (@code{HMGL} gr, @code{double} x1, @code{double} x2, @code{double} y1, @code{double} y2, @code{double} z1, @code{double} z2)
 @end ifclear
 Sets the ranges of coordinates. If minimal and maximal values of the coordinate are the same then they are ignored. Also it sets the range for coloring (analogous to @code{crange z1 z2}). This is default color range for 2d plots. Initial ranges are [-1, 1].
 @end deftypefn
@@ -571,6 +571,13 @@ Sets the ranges of coordinates. If minimal and maximal values of the coordinate
 @deftypefnx {Method on @code{mglGraph}} @code{void} SetRanges (@code{const mglDataA &}xx, @code{const mglDataA &}yy, @code{const mglDataA &}zz, @code{const mglDataA &}cc)
 Sets the ranges of @samp{x}-, at samp{y}-, at samp{z}-coordinates and coloring as minimal and maximal values of data @var{xx}, @var{yy}, @var{zz}, @var{cc} correspondingly.
 @end deftypefn
+
+ at deftypefn {Method on @code{mglGraph}} @code{void} SetAutoRanges (@code{mglPoint} p1, @code{mglPoint} p2)
+ at deftypefnx {Method on @code{mglGraph}} @code{void} SetAutoRanges (@code{double} x1, @code{double} x2, @code{double} y1, @code{double} y2, @code{double} z1=@code{0}, @code{double} z2=@code{0}, @code{double} c1=@code{0}, @code{double} c2=@code{0})
+ at deftypefnx {C function} @code{void} mgl_set_auto_ranges (@code{HMGL} gr, @code{double} x1, @code{double} x2, @code{double} y1, @code{double} y2, @code{double} z1, @code{double} z2, @code{double} z1, @code{double} z2)
+Sets the ranges for automatic coordinates. If minimal and maximal values of the coordinate are the same then they are ignored.
+ at end deftypefn
+
 @end ifclear
 
 @anchor{origin}
@@ -661,6 +668,7 @@ Use @code{Ternary(0)} for returning to usual axis. @sref{Ternary axis} @sref{Axi
 @cindex SetTickTempl
 @cindex SetTickRotate
 @cindex SetTickSkip
+ at cindex SetOriginTick
 @end ifclear
 
 @anchor{adjust}
@@ -716,6 +724,7 @@ Set the manual positions @var{val} and its labels @var{lbl} for ticks along axis
 Set template @var{templ} for x-,y-,z-axis ticks or colorbar ticks. It may contain TeX symbols also. If @var{templ}=@code{""} then default template is used (in simplest case it is @samp{%.2g}). Setting on template switch off automatic ticks tuning.
 @end deftypefn
 
+ at anchor{ticktime}
 @deftypefn {MGL command} {} ticktime 'dir' [@code{dv} 'tmpl']
 @ifclear UDAV
 @deftypefnx {Method on @code{mglGraph}} @code{void} SetTicksTime (@code{char} dir, @code{mreal} val, @code{const char *}templ)
@@ -730,6 +739,7 @@ Gets number of seconds from 1970 year to specified date/time @var{str}. The form
 @end deftypefn
 @end ifclear
 
+ at anchor{tuneticks}
 @deftypefn {MGL command} {} tuneticks @code{val} [@code{pos=1.15}]
 @ifclear UDAV
 @deftypefnx {Method on @code{mglGraph}} @code{void} SetTuneTicks (@code{int} tune, @code{mreal} pos=@code{1.15})
@@ -738,13 +748,17 @@ Gets number of seconds from 1970 year to specified date/time @var{str}. The form
 Switch on/off ticks enhancing by factoring common multiplier (for small, like from 0.001 to 0.002, or large, like from 1000 to 2000, coordinate values -- enabled if @var{tune}&1 is nonzero) or common component (for narrow range, like from 0.999 to 1.000 -- enabled if @var{tune}&2 is nonzero). Also set the position @var{pos} of common multiplier/component on the axis: =0 at minimal axis value, =1 at maximal axis value. Default value is 1.15.
 @end deftypefn
 
+ at anchor{tickshift}
+ at deftypefn {MGL command} {} tickshift @code{dx [dy=0 dz=0 dc=0]}
 @ifclear UDAV
-
- at deftypefn {Method on @code{mglGraph}} @code{void} SetTickShift (@code{mglPoint} d)
+ at deftypefnx {Method on @code{mglGraph}} @code{void} SetTickShift (@code{mglPoint} d)
 @deftypefnx {C function} @code{void} mgl_set_tick_shift (@code{HMGL} gr, @code{mreal} dx, @code{mreal} dy, @code{mreal} dz, @code{mreal} dc)
+ at end ifclear
 Set value of additional shift for ticks labels.
 @end deftypefn
 
+ at ifclear UDAV
+
 @deftypefn {Method on @code{mglGraph}} @code{void} SetTickRotate (@code{bool} val)
 @deftypefnx {C function} @code{void} mgl_set_tick_rotate (@code{HMGL} gr, @code{bool} val)
 Enable/disable ticks rotation if there are too many ticks or ticks labels are too long.
@@ -762,6 +776,14 @@ Enable/disable using UTC time for ticks labels. In C/Fortran you can use @code{m
 
 @end ifclear
 
+ at anchor{origintick}
+ at deftypefn {MGL command} {} origintick @code{val}
+ at ifclear UDAV
+ at deftypefnx {Method on @code{mglGraph}} @code{void} SetOriginTick (@code{bool} val=@code{true})
+ at end ifclear
+Enable/disable drawing of ticks labels at axis origin. In C/Fortran you can use @code{mgl_set_flag(gr,val, MGL_NO_ORIGIN);}.
+ at end deftypefn
+
 @anchor{ticklen}
 @deftypefn {MGL command} {} ticklen @code{val} [@code{stt=1}]
 @ifclear UDAV
diff --git a/texinfo/core_ru.texi b/texinfo/core_ru.texi
index 05463ac..132a06c 100644
--- a/texinfo/core_ru.texi
+++ b/texinfo/core_ru.texi
@@ -556,6 +556,12 @@ Setsize: размер(ы) равны нулю или отрицательны
 @deftypefnx {Метод класса @code{mglGraph}} @code{void} SetRanges (@code{const mglDataA &}xx, @code{const mglDataA &}yy, @code{const mglDataA &}zz, @code{const mglDataA &}cc)
 Задает диапазон изменения @samp{x}-, at samp{y}-, at samp{z}-, at samp{c}-координат как минимальное и максимальное значение массивов @var{xx}, @var{yy}, @var{zz}, @var{cc} соответственно.
 @end deftypefn
+
+ at deftypefn {Метод класса @code{mglGraph}} @code{void} SetAutoRanges (@code{mglPoint} p1, @code{mglPoint} p2)
+ at deftypefnx {Метод класса @code{mglGraph}} @code{void} SetAutoRanges (@code{double} x1, @code{double} x2, @code{double} y1, @code{double} y2, @code{double} z1=@code{0}, @code{double} z2=@code{0}, @code{double} c1=@code{0}, @code{double} c2=@code{0})
+ at deftypefnx {Функция С} @code{void} mgl_set_auto_ranges (@code{HMGL} gr, @code{double} x1, @code{double} x2, @code{double} y1, @code{double} y2, @code{double} z1, @code{double} z2, @code{double} z1, @code{double} z2)
+Задает диапазон изменения координат для автоматических переменных. Если минимальное и максимальное значение координаты равны, то они игнорируются по данному направлению.
+ at end deftypefn
 @end ifclear
 
 @anchor{origin}
@@ -646,6 +652,7 @@ Ternary -- специальный тип графика для 3 зависим
 @cindex SetTickTempl
 @cindex SetTickRotate
 @cindex SetTickSkip
+ at cindex SetOriginTick
 @end ifclear
 
 @anchor{adjust}
@@ -701,6 +708,7 @@ Ternary -- специальный тип графика для 3 зависим
 Задает шаблон @var{templ} для меток вдоль x-,y-,z-оси или colorbar. Шаблон может содержать и символы TeX. Если @var{templ}=@code{""}, то используется шаблон по умолчанию (в простейшем случае @samp{%.2g}). Установка шаблона выключает автоматическое улучшение вида меток.
 @end deftypefn
 
+ at anchor{ticktime}
 @deftypefn {Команда MGL} {} ticktime 'dir' [@code{dv} 'tmpl']
 @ifclear UDAV
 @deftypefnx {Метод класса @code{mglGraph}} @code{void} SetTicksTime (@code{char} dir, @code{mreal} val, @code{const char *}templ)
@@ -715,6 +723,7 @@ Ternary -- специальный тип графика для 3 зависим
 @end deftypefn
 @end ifclear
 
+ at anchor{tuneticks}
 @deftypefn {Команда MGL} {} tuneticks @code{val} [@code{pos=1.15}]
 @ifclear UDAV
 @deftypefnx {Метод класса @code{mglGraph}} @code{void} SetTuneTicks (@code{int} tune, @code{mreal} pos=@code{1.15})
@@ -723,13 +732,17 @@ Ternary -- специальный тип графика для 3 зависим
 Включает/выключает улучшение вида меток осей путем вынесения общего множителя (для маленьких, типа 0.001...0.002, или больших, типа 1000...2000, значений координат) или общей компоненты (для узкого диапазона, типа 0.999...1.000). Также задает положение @var{pos} общего множителя на оси: =0 около минимального значения, =1 около максимального значения.
 @end deftypefn
 
+ at anchor{tickshift}
+ at deftypefn {Команда MGL} {} tickshift @code{dx [dy=0 dz=0 dc=0]}
 @ifclear UDAV
-
- at deftypefn {Метод класса @code{mglGraph}} @code{void} SetTickShift (@code{mglPoint} d)
+ at deftypefnx {Метод класса @code{mglGraph}} @code{void} SetTickShift (@code{mglPoint} d)
 @deftypefnx {Функция С} @code{void} mgl_set_tick_shift (@code{HMGL} gr, @code{mreal} dx, @code{mreal} dy, @code{mreal} dz, @code{mreal} dc)
+ at end ifclear
 Задает значение дополнительного сдвига меток осей координат.
 @end deftypefn
 
+ at ifclear UDAV
+
 @deftypefn {Метод класса @code{mglGraph}} @code{void} SetTickRotate (@code{bool} val)
 @deftypefnx {Функция С} @code{void} mgl_set_tick_rotate (@code{HMGL} gr, @code{bool} val)
 Включает/выключает поворот меток если их число или длина меток слишком велики.
@@ -741,12 +754,19 @@ Ternary -- специальный тип графика для 3 зависим
 @end deftypefn
 
 @deftypefn {Метод класса @code{mglGraph}} @code{void} SetTimeUTC (@code{bool} val)
- at c @deftypefnx {Функция С} @code{void} mgl_set_tick_skip (@code{HMGL} gr, @code{bool} val)
 Разрешает/запрещает использование UTC времени в метках осей координат. В C/Fortran следует использовать @code{mgl_set_flag(gr,val, MGL_USE_GMTIME);}.
 @end deftypefn
 
 @end ifclear
 
+ at anchor{origintick}
+ at deftypefn {Команда MGL} {} origintick @code{val}
+ at ifclear UDAV
+ at deftypefnx {Метод класса @code{mglGraph}} @code{void} SetOriginTick (@code{bool} val=@code{true})
+ at end ifclear
+Разрешает/запрещает рисование меток в точке пересечения осей координат. В C/Fortran следует использовать @code{mgl_set_flag(gr,val, MGL_NO_ORIGIN);}.
+ at end deftypefn
+
 @anchor{ticklen}
 @deftypefn {Команда MGL} {} ticklen @code{val} [@code{stt=1}]
 @ifclear UDAV
diff --git a/texinfo/version_hist.txt b/texinfo/version_hist.txt
index b43e928..fa59be6 100644
--- a/texinfo/version_hist.txt
+++ b/texinfo/version_hist.txt
@@ -1,40 +1,40 @@
-2.1.2 Released 28 January 2013
-2.1.1 Released 24 December 2012
-2.1 Released 13 December 2012
-2.0.3 Released 27 July 2012
-2.0.2 Released 24 May 2012
-2.0.1 Released 23 May 2012
-2.0 Released 12 April 2012
-2.0.b Released 23 August 2011
+2.1.2	Released 28 January 2013
+2.1.1	Released 24 December 2012
+2.1 	Released 13 December 2012
+2.0.3	Released 27 July 2012
+2.0.2	Released 24 May 2012
+2.0.1	Released 23 May 2012
+2.0 	Released 12 April 2012
+2.0.b	Released 23 August 2011
 
-1.11.3 Released 23 November 2012
-1.11.2 Released 30 May 2011
-1.11.1 Released 28 March 2011
-1.11 Released 8 November 2010
+1.11.3	Released 23 November 2012
+1.11.2	Released 30 May 2011
+1.11.1	Released 28 March 2011
+1.11	Released 8 November 2010
 1.10.2.1 Released 27 March 2010
-1.10.2 Released 22 March 2010
-1.10.1 Released 8 March 2010
-1.10 Released 28 December 2009
-1.9 Released 8 July 2009
-1.8.1 Released 4 March 2009
-1.8 Released 27 November 2008
-1.7 Released 5 June 2008
-1.6.2 Released 5 April 2008
-1.6.1 Released 2 April 2008
-1.6 Released 17 March 2008
-1.5 Released 11 January 2008
-1.4.3.1 Released 24 December 2008
-1.4.3 Released 18 December 2008
-1.4.2 Released 27 November 2007
-1.4.1 Released 14 November 2007
-1.4 Released 30 October 2007
-1.3	Released 15 October 2007
+1.10.2	Released 22 March 2010
+1.10.1	Released 8 March 2010
+1.10	Released 28 December 2009
+1.9 	Released 8 July 2009
+1.8.1	Released 4 March 2009
+1.8 	Released 27 November 2008
+1.7 	Released 5 June 2008
+1.6.2	Released 5 April 2008
+1.6.1	Released 2 April 2008
+1.6 	Released 17 March 2008
+1.5 	Released 11 January 2008
+1.4.3.1	Released 24 December 2008
+1.4.3	Released 18 December 2008
+1.4.2	Released 27 November 2007
+1.4.1	Released 14 November 2007
+1.4 	Released 30 October 2007
+1.3 	Released 15 October 2007
 1.2.2	Released 26 September 2007
 1.2.1	Released 14 September 2007
-1.2	Released 10 September 2007
-1.1	Released 23 May 2007
-1.0	Released 2 April 2007
+1.2 	Released 10 September 2007
+1.1 	Released 23 May 2007
+1.0 	Released 2 April 2007
 
-0.9	Last beta version of the MathGL library. Released 2 March 2007
+0.9		Last beta version of the MathGL library. Released 2 March 2007
 0.8.1	Released 19 Febriary 2007
 0.8.0	First public release (24 January 2007)
diff --git a/texinfo/web_en.texi b/texinfo/web_en.texi
index de7138d..c5fd42e 100644
--- a/texinfo/web_en.texi
+++ b/texinfo/web_en.texi
@@ -423,8 +423,8 @@ You may download current version of MathGL for following configurations:
 @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}-mingw.i686.7z,Win32 GPL} binaries for MinGW (build for i686)
 @item
 @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.LGPL-mingw.i686.7z,Win32 LGPL} binaries for MinGW (build for i686, no GSL and HDF5 support)
- at item
- at uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.LGPL-win64.7z,Win64 LGPL} binaries for MSVS 2010 (no GSL and HDF5 support)
+ at c @item
+ at c @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.LGPL-win64.7z,Win64 LGPL} binaries for MSVS 2010 (no GSL and HDF5 support)
 @item
 @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.eng.pdf,PDF} documentation in English
 @item
diff --git a/texinfo/web_ru.texi b/texinfo/web_ru.texi
index de7138d..c5fd42e 100644
--- a/texinfo/web_ru.texi
+++ b/texinfo/web_ru.texi
@@ -423,8 +423,8 @@ You may download current version of MathGL for following configurations:
 @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}-mingw.i686.7z,Win32 GPL} binaries for MinGW (build for i686)
 @item
 @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.LGPL-mingw.i686.7z,Win32 LGPL} binaries for MinGW (build for i686, no GSL and HDF5 support)
- at item
- at uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.LGPL-win64.7z,Win64 LGPL} binaries for MSVS 2010 (no GSL and HDF5 support)
+ at c @item
+ at c @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.LGPL-win64.7z,Win64 LGPL} binaries for MSVS 2010 (no GSL and HDF5 support)
 @item
 @uref{http://downloads.sourceforge.net/mathgl/mathgl-@value{VERSION}.eng.pdf,PDF} documentation in English
 @item
diff --git a/todo.txt b/todo.txt
new file mode 100644
index 0000000..d65b8a3
--- /dev/null
+++ b/todo.txt
@@ -0,0 +1,84 @@
+http://ubuntuforums.org/showthread.php?t=1862084
+Device 0 (VID=0502 and PID=337d) is UNKNOWN. (VID=18d1 and PID=4e41)
+
+============= NEW FOR LATER =============
+
+A. Paper about MathGL!!!
+
+1. Export to X3D, COLLADA !!!
+2. GTK window/widgets
+3. Labels at TriCont()
+4. Drawing of saddle quadrangles
+5. 2D textures as one of standard way for coloring -- if '%' present in color scheme
+6. Graph(mreal x, mreal y, names, styles, font, size); -- names = "node1[node2,node3,node4[node5,node6]]", styles -- the same + apply for subnodes if absent. Styles are colors (fill,border,line), dash (to subnodes), marks "dos^v><", arrows.
+7. Use libtcc for mglFormula only! -- note that MGL formulas (i.e. for mglData) are fast enough. The only possible speeding up for Fill() and Modify() functions
+8. 3D text (with depth, for Quality=3)
+9. Text along 3D curve (for Quality=3)
+10. introduce new primitive type=5 -- arrow for Quality&3!=3. for better drawing in projections + json/view ?!?
+11. enable line width for mesh and fall???
+12. WX window/widgets
+13. Textured brushes for drawing: (a) as 8*8 array, i.e. long (save only index in mglPrim::type); (b) BrushWidth; (c) BrushAngle; (d) some pixels become transparent; (e) count from edge of image; (f) symbols {+-;=\*<>^osdiODSI}. Question: how this can be exported???
+
+14. New tests:
+	b. Add test for curved text align "L","C","R"
+	c. Tests for mglDataC arrays (as separate flag)
+	e. Test for Crop, Momentum, NormSl, Sew, DiffParam, Envelope, FFT, STFA, Transform for all directions "xyz"; Clean, Last, First, Find, Spline3, FindAny, Insert, Delete, Put, Read/Save+HDF, SetId/Column, PrintInfo, Squeeze, Extend, Trace, Combine, new Max/Min/Momentum, FillSample, Hist, operators, Sort, Export/Import, Jacobian
+	h. Test for CalcXYZ -- in data samples
+	i. Tests for Quality!=2
+	l. Test Error for all marks
+	o. Add Fortran tests?
+	p. Test FaceNum for Surf
+	q. Test FSurf
+	u. Test FlowP + 3d
+	w. Test diffuse, specular local sources
+
+15. Check samples: colorbar
+16. Add samples about data handling (data1, data2, data2)
+
+20. JS rotation in Firefox
+23. check if all "value" options are described
+
+============= UDAV =============
+
+01. Show plot at creation stage (if option is enabled -- can be long process!!!) + auto axis range or [-1,1] by default
+
+02. Double click -> setup dialog (optionally -- after QTreeWidgetItem)
+
+03. Dialog for InPlot(s):
+{SubPlot,MultiPlot}	-- enable rotate,aspect,title
+{ColumnPlot}		-- enable rotate,several
+{StickPlot}		-- enable several
+{InPlot}		-- enable rotate,aspect,title
+Image with gray alternatives and black choice
+
+04. QTreeWidgetItem -- ICON (func/call,if,for,once,subplot,...); annotation/name; (keep LINE - POS in script). After editing/changing --> put text to editor.
+Buttons: new,load,save // newcmd,hide,annotation,collapse on/off,setup // calc. Click on item cause change of line number in editor.
+05. Group/ungroup if command in single line, separated by ':'
+06. Hide plot/group by putting "#h " at beginning of line
+07. Annotation of plot/group -- comment before it
+08. Drag&drop plot/group between inplots (in tree widget)
+
+09. Create default plot dependently of selected row/column/range in data table
+10. Data change via menu/toolbar -- save to internal script => enable undo + save initial array(?)
+
+11. Speed up rotation like as done in JavaScript
+
+12. Multi-threading for enabling "stop".
+
+13. Manual rotate (i.e. change rotate arguments) by spinboxes ???
+
+14. Close button on data tabs ?!?
+
+============= UNSURE ===========
+
+1. Problem with \calB and so on (\calH, ...) -- they are present only in italic font :(.
+2. create PWT fonts
+3. \dfrac for normal size and centering  (sample \big\big{\frac{1}{2}}) ???
+4. "Cut off" curves if text is printed inside it (for contour labels) ?!?
+5. String variables in MGL + readstr command.
+6. Pool of threads for speeding up ??? or openmp ???
+7. Read DICOM files
+8. Check RunThr() in python/octave
+9. Try libtcc (TinyCC) as alternative to MGL -- how to handle ^ operator???
+10. Auto axis range for formulas, like AutoRange("y(x)") or AutoRange('x',"x(t)").
+11. Use Hershey as built-in font ??? -- for smaller size only
diff --git a/udav/CMakeLists.txt b/udav/CMakeLists.txt
index 36ff0dd..90c1df6 100644
--- a/udav/CMakeLists.txt
+++ b/udav/CMakeLists.txt
@@ -26,6 +26,7 @@ qt4_add_resources(udav_rc_src ${udav_rc} )
 #qt_wrap_cpp(udav udav_moc_src ${udav_moc_hdr} )
 qt4_wrap_cpp(udav_moc_src ${udav_moc_hdr} )
 add_executable(udav ${udav_src} ${udav_moc_src} ${udav_rc_src})
+#set_target_properties(udav PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
 target_link_libraries(udav mgl-qt ${QT_LIBRARIES})
 
 if(MGL_HAVE_PTHREAD)
diff --git a/udav/open_dlg.cpp b/udav/open_dlg.cpp
index 8552f5f..8627d42 100644
--- a/udav/open_dlg.cpp
+++ b/udav/open_dlg.cpp
@@ -54,7 +54,7 @@ DataOpenDialog::DataOpenDialog(QWidget *parent) : QDialog(parent)
 
 	a = new QHBoxLayout;	o->addLayout(a);
 	l = new QLabel(tr("Data name"));	a->addWidget(l);
-	char buf[32];	sprintf(buf,"mgl_%d",numDataOpened);
+	char buf[32];	snprintf(buf,32,"mgl_%d",numDataOpened);
 	name = new QLineEdit(buf,this);		a->addWidget(name);
 
 	rA = new QRadioButton(tr("Auto detect data sizes"), this);
@@ -114,7 +114,7 @@ void DataOpenDialog::prepareResult()
 {
 	code = "";	numDataOpened++;	data = name->text();
 	// prepare unique value of name for next time
-	char buf[32];	sprintf(buf,"mgl_%d",numDataOpened);	name->setText(buf);
+	char buf[32];	snprintf(buf,32,"mgl_%d",numDataOpened);	name->setText(buf);
 	mglVar *v = parser.AddVar(data.toAscii().constData());
 	bool dd=0;
 	if(rA->isChecked())	//	auto sizes
diff --git a/udav/text_pnl.cpp b/udav/text_pnl.cpp
index 19f8889..2019a1b 100644
--- a/udav/text_pnl.cpp
+++ b/udav/text_pnl.cpp
@@ -253,11 +253,7 @@ void TextPanel::newCmd(int n)
 	newCmdDlg->show();
 }
 //-----------------------------------------------------------------------------
-#if MGL_HAVE_HDF5==0
-void TextPanel::saveHDF5(const QString &fileName){}
-void TextPanel::loadHDF5(const QString &fileName){}
-//-----------------------------------------------------------------------------
-#else
+#if MGL_HAVE_HDF5
 #define H5_USE_16_API
 #include <hdf5.h>
 void TextPanel::loadHDF5(const QString &fileName)
@@ -376,6 +372,9 @@ void TextPanel::saveHDF5(const QString &fileName)
 	setStatus(tr("File %1 saved").arg(fileName));
 	return;
 }
+#else
+void TextPanel::saveHDF5(const QString &fileName){}
+void TextPanel::loadHDF5(const QString &fileName){}
 #endif
 //-----------------------------------------------------------------------------
 void TextPanel::load(const QString &fileName)
diff --git a/utils/mglconv.cpp b/utils/mglconv.cpp
index 2c8d54f..f4abba8 100644
--- a/utils/mglconv.cpp
+++ b/utils/mglconv.cpp
@@ -72,12 +72,12 @@ int main(int argc, char *argv[])
 				"\t-h           print this message\n" );
 			ch = 'h';	break;
 		}
-		else if(ch=='o')	strcpy(oname, optarg);
+		else if(ch=='o')	strncpy(oname, optarg,256);
 		else if(ch==-1 && optind<argc)
-		{	strcpy(iname, argv[optind][0]=='-'?"":argv[optind]);	break;	}
+		{	strncpy(iname, argv[optind][0]=='-'?"":argv[optind],256);	break;	}
 	}
 	if(ch=='h')	return 0;
-	if(*oname==0)	{	strcpy(oname,*iname?iname:"out");	strcat(oname,".png");	}
+	if(*oname==0)	{	strncpy(oname,*iname?iname:"out",250);	strcat(oname,".png");	}
 	
 	mgl_ask_func = mgl_ask_gets;
 	// prepare for animation
@@ -116,7 +116,7 @@ int main(int argc, char *argv[])
 			p.Execute(&gr,str.c_str());
 			if(gr.Message()[0])	printf("%s\n",gr.Message());
 			gr.EndFrame();
-			sprintf(buf,"%s-%ld",oname,i);
+			snprintf(buf,2048,"%s-%ld",oname,i);
 			if(!gif)	gr.WriteFrame(buf);
 		}
 		if(gif)	gr.CloseGIF();
diff --git a/utils/mglview.cpp b/utils/mglview.cpp
index 4094294..34d24a1 100644
--- a/utils/mglview.cpp
+++ b/utils/mglview.cpp
@@ -60,7 +60,7 @@ int main(int argc, char **argv)
 			ch = 'h';	break;
 		}
 		else if(ch==-1 && optind<argc)
-		{	strcpy(iname, argv[optind][0]=='-'?"":argv[optind]);	break;	}
+		{	strncpy(iname, argv[optind][0]=='-'?"":argv[optind],256);	break;	}
 	}
 	if(ch=='h')	return 0;
 
diff --git a/widgets/CMakeLists.txt b/widgets/CMakeLists.txt
index 9f4ede7..90ea00c 100644
--- a/widgets/CMakeLists.txt
+++ b/widgets/CMakeLists.txt
@@ -8,12 +8,18 @@ if(MGL_HAVE_FLTK)
 	set_target_properties(mgl-fltk PROPERTIES SOVERSION 7.0.0)
 	set_target_properties(mgl-fltk PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-fltk PROPERTIES DEFINE_SYMBOL "mgl_EXPORTS")
-	set_target_properties(mgl-fltk-static PROPERTIES OUTPUT_NAME "mgl-fltk")
 	set_target_properties(mgl-fltk-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-fltk-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
 	target_link_libraries(mgl-fltk mgl)
 	target_link_libraries(mgl-fltk ${FLTK_LIBRARIES})
 
+	if(enable-mgl2)
+		set_target_properties(mgl-fltk PROPERTIES OUTPUT_NAME "mgl2-fltk")
+		set_target_properties(mgl-fltk-static PROPERTIES OUTPUT_NAME "mgl2-fltk")
+	else(enable-mgl2)
+		set_target_properties(mgl-fltk-static PROPERTIES OUTPUT_NAME "mgl-fltk")
+	endif(enable-mgl2)
+
 	install(
 		TARGETS mgl-fltk mgl-fltk-static
 		RUNTIME DESTINATION bin
@@ -29,12 +35,18 @@ if(MGL_HAVE_GLUT)
 	set_target_properties(mgl-glut PROPERTIES SOVERSION 7.0.0)
 	set_target_properties(mgl-glut PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-glut PROPERTIES DEFINE_SYMBOL "mgl_EXPORTS")
-	set_target_properties(mgl-glut-static PROPERTIES OUTPUT_NAME "mgl-glut")
 	set_target_properties(mgl-glut-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-glut-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
 	target_link_libraries(mgl-glut mgl)
 	target_link_libraries(mgl-glut ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES})
 
+	if(enable-mgl2)
+		set_target_properties(mgl-glut PROPERTIES OUTPUT_NAME "mgl2-glut")
+		set_target_properties(mgl-glut-static PROPERTIES OUTPUT_NAME "mgl2-glut")
+	else(enable-mgl2)
+		set_target_properties(mgl-glut-static PROPERTIES OUTPUT_NAME "mgl-glut")
+	endif(enable-mgl2)
+
 	install(
 		TARGETS mgl-glut mgl-glut-static
 		RUNTIME DESTINATION bin
@@ -50,12 +62,18 @@ if(MGL_HAVE_WX)
 	set_target_properties(mgl-wx PROPERTIES SOVERSION 7.0.0)
 	set_target_properties(mgl-wx PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-wx PROPERTIES DEFINE_SYMBOL "mgl_EXPORTS")
-	set_target_properties(mgl-wx-static PROPERTIES OUTPUT_NAME "mgl-wx")
 	set_target_properties(mgl-wx-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-wx-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
 	target_link_libraries(mgl-wx mgl)
 	target_link_libraries(mgl-wx ${wxWidgets_LIBRARIES})
 
+	if(enable-mgl2)
+		set_target_properties(mgl-wx PROPERTIES OUTPUT_NAME "mgl2-wx")
+		set_target_properties(mgl-wx-static PROPERTIES OUTPUT_NAME "mgl2-wx")
+	else(enable-mgl2)
+		set_target_properties(mgl-wx-static PROPERTIES OUTPUT_NAME "mgl-wx")
+	endif(enable-mgl2)
+
 	install(
 		TARGETS mgl-wx mgl-wx-static
 		RUNTIME DESTINATION bin
@@ -72,12 +90,18 @@ if(MGL_HAVE_QT)
 	set_target_properties(mgl-qt PROPERTIES SOVERSION 7.0.0)
 	set_target_properties(mgl-qt PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-qt PROPERTIES DEFINE_SYMBOL "mgl_EXPORTS")
-	set_target_properties(mgl-qt-static PROPERTIES OUTPUT_NAME "mgl-qt")
 	set_target_properties(mgl-qt-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-qt-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
 	target_link_libraries(mgl-qt mgl)
 	target_link_libraries(mgl-qt ${QT_LIBRARIES})
 
+	if(enable-mgl2)
+		set_target_properties(mgl-qt PROPERTIES OUTPUT_NAME "mgl2-qt")
+		set_target_properties(mgl-qt-static PROPERTIES OUTPUT_NAME "mgl2-qt")
+	else(enable-mgl2)
+		set_target_properties(mgl-qt-static PROPERTIES OUTPUT_NAME "mgl-qt")
+	endif(enable-mgl2)
+
 	install(
 		TARGETS mgl-qt mgl-qt-static
 		RUNTIME DESTINATION bin
@@ -102,10 +126,16 @@ if(MGL_HAVE_QT AND MGL_HAVE_FLTK)
 	set_target_properties(mgl-wnd PROPERTIES SOVERSION 7.0.0)
 	set_target_properties(mgl-wnd PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-wnd PROPERTIES DEFINE_SYMBOL "mgl_EXPORTS")
-	set_target_properties(mgl-wnd-static PROPERTIES OUTPUT_NAME "mgl-wnd")
 	set_target_properties(mgl-wnd-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
 	set_target_properties(mgl-wnd-static PROPERTIES COMPILE_FLAGS -DMGL_STATIC_DEFINE)
 
+	if(enable-mgl2)
+		set_target_properties(mgl-wnd PROPERTIES OUTPUT_NAME "mgl2-wnd")
+		set_target_properties(mgl-wnd-static PROPERTIES OUTPUT_NAME "mgl2-wnd")
+	else(enable-mgl2)
+		set_target_properties(mgl-wnd-static PROPERTIES OUTPUT_NAME "mgl-wnd")
+	endif(enable-mgl2)
+
 	target_link_libraries(mgl-wnd mgl)
 	target_link_libraries(mgl-wnd ${QT_LIBRARIES})
 #	target_link_libraries(mgl-wnd ${wxWidgets_LIBRARIES})
diff --git a/widgets/fltk.cpp b/widgets/fltk.cpp
index c267ac9..60649c4 100644
--- a/widgets/fltk.cpp
+++ b/widgets/fltk.cpp
@@ -198,7 +198,7 @@ int Fl_MathGL::handle(int code)
 			mglPoint p = gr->CalcXYZ(Fl::event_x()-x(), Fl::event_y()-y());
 			if(g)	g->LastMousePos = p;
 			char s[128];
-			sprintf(s,"x=%g, y=%g, z=%g",p.x,p.y,p.z);
+			snprintf(s,128,"x=%g, y=%g, z=%g",p.x,p.y,p.z);
 			draw();	fl_color(FL_BLACK);		fl_draw(s,40,70);
 		}
 	}
diff --git a/widgets/glut.cpp b/widgets/glut.cpp
index 3c97118..3a52d3d 100644
--- a/widgets/glut.cpp
+++ b/widgets/glut.cpp
@@ -149,19 +149,19 @@ void _mgl_key_up(unsigned char ch,int ,int )
 	if(ch=='P')
 	{
 		char str[128];
-		sprintf(str,"%s_%d.png",_mgl_glwnd->PlotId.c_str(),_mgl_glwnd->curr_fig);
+		snprintf(str,128,"%s_%d.png",_mgl_glwnd->PlotId.c_str(),_mgl_glwnd->curr_fig);
 		mgl_write_png(_mgl_glwnd, str, "Math GL");
 	}
 	if(ch=='J')
 	{
 		char str[128];
-		sprintf(str,"%s_%d.jpg",_mgl_glwnd->PlotId.c_str(),_mgl_glwnd->curr_fig);
+		snprintf(str,128,"%s_%d.jpg",_mgl_glwnd->PlotId.c_str(),_mgl_glwnd->curr_fig);
 		mgl_write_jpg(_mgl_glwnd, str, "Math GL");
 	}
 	if(ch=='E')
 	{
 		char str[128];
-		sprintf(str,"%s_%d.eps",_mgl_glwnd->PlotId.c_str(),_mgl_glwnd->curr_fig);
+		snprintf(str,128,"%s_%d.eps",_mgl_glwnd->PlotId.c_str(),_mgl_glwnd->curr_fig);
 		mgl_write_eps(_mgl_glwnd, str, "Math GL");
 	}
 	if(ch==' ')	_mgl_glwnd->Clf();
diff --git a/widgets/qt.cpp b/widgets/qt.cpp
index 970f2f9..f13dcbc 100644
--- a/widgets/qt.cpp
+++ b/widgets/qt.cpp
@@ -90,7 +90,7 @@ void MGL_EXPORT mgl_ask_qt(const wchar_t *quest, wchar_t *res)
 QMathGL::QMathGL(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f)
 {
 	autoResize = false;	draw_par = 0;	draw_func = 0;
-	gr = new mglCanvas;		appName = "MathGL";
+	gr = new mglCanvas;	appName = "MathGL";
 	popup = 0;	grBuf = 0;	draw = 0;
 	phi = tet = per = 0;
 	x1 = y1 = ax1 = ay1 = 0;	x2 = y2 = ax2 = ay2 = 1;
@@ -298,13 +298,13 @@ void QMathGL::refresh()
 	{
 		if(draw_func || draw)
 		{
-#if MGL_HAVE_PTHREAD
+/*#if MGL_HAVE_PTHREAD	// TODO add qthread here later
 			pthread_t tmp;
 			pthread_create(&tmp, 0, mgl_qt_thr, this);
 			pthread_join(tmp, 0);
-#else
+#else*/
 			draw_thr();
-#endif
+/*#endif*/
 		}
 		mgl_zoom(gr,x1,y1,x2,y2);	mgl_perspective(gr,per);
 		if(viewYZ)	mgl_view(gr,0,phi,tet);

-- 
Packaging for mathgl



More information about the debian-science-commits mailing list