Imported Upstream version 2.2.2+svn930
authorDimitrios Eftaxiopoulos <eftaxi12@otenet.gr>
Sat, 15 Mar 2014 09:41:40 +0000 (11:41 +0200)
committerDimitrios Eftaxiopoulos <eftaxi12@otenet.gr>
Sat, 15 Mar 2014 09:41:40 +0000 (11:41 +0200)
31 files changed:
CMakeLists.txt
ChangeLog.txt
examples/fltk_example.cpp
examples/full_test.cpp
examples/glut_example.cpp
examples/qt_example.cpp
examples/samples.cpp
include/mgl2/data.h
include/mgl2/datac.h
include/mgl2/define.h
include/mgl2/glut.h
json/index.html
json/sylvester.js [deleted file]
json/sylvester.src.js [new file with mode: 0644]
src/axis.cpp
src/base.cpp
src/data.cpp
src/data_io.cpp
src/data_png.cpp
src/export_2d.cpp
src/vect.cpp
texinfo/core_en.texi
texinfo/core_ru.texi
texinfo/example_en.texi
texinfo/example_ru.texi
texinfo/version.texi
texinfo/version_hist.txt
texinfo/web_en.texi
texinfo/web_ru.texi
todo.txt
widgets/glut.cpp

index 261117004f62fde4a85815823034c565b10f6107..f279dce3fc742612762db5d22ba557a9137089aa 100644 (file)
@@ -9,7 +9,7 @@ endif(NOT CMAKE_BUILD_TYPE)
 
 set(CMAKE_VERBOSE_MAKEFILE ON)
 set(MathGL_VERSION_MAJOR 2)
-set(MathGL_VERSION_MINOR 2.1)
+set(MathGL_VERSION_MINOR 2.2)
 set(MathGL_SOVERSION 7.2.0)
 
 
@@ -152,6 +152,10 @@ else(enable-double)
        set(MGL_USE_DOUBLE 0)
 endif(enable-double)
 
+if(enable-qt4 OR enable-qt5)
+       set(MGL_HAVE_QT 1)
+endif(enable-qt4 OR enable-qt5)
+
 if(enable-simple)
        set(MGL_NO_DATA_A 1)
 message(STATUS "Class mglDataA is switched off.")
@@ -512,16 +516,19 @@ endif(WIN32)
 add_subdirectory( src )
 add_subdirectory( widgets )
 add_subdirectory( include )
-add_subdirectory( udav )
-add_subdirectory( json )
-#add_subdirectory( mgllab )
-add_subdirectory( lang )
+if(NOT enable-lgpl)
+       add_subdirectory( udav )
+       add_subdirectory( json )
+       add_subdirectory( lang )
+       add_subdirectory( utils )
+#      add_subdirectory( mgllab )
+endif(NOT enable-lgpl)
+
 if(NOT MSVC AND NOT BORLAND)
-add_subdirectory( utils )
-add_subdirectory( examples )
+       add_subdirectory( examples )
 
-if(MGL_HAVE_DOC_HTML OR MGL_HAVE_DOC_SITE OR MGL_HAVE_DOC_INFO OR MGL_HAVE_DOC_PDF_RU OR MGL_HAVE_DOC_PDF_EN )
-add_subdirectory( texinfo )
-endif(MGL_HAVE_DOC_HTML OR MGL_HAVE_DOC_SITE OR MGL_HAVE_DOC_INFO OR MGL_HAVE_DOC_PDF_RU OR MGL_HAVE_DOC_PDF_EN )
+       if(MGL_HAVE_DOC_HTML OR MGL_HAVE_DOC_SITE OR MGL_HAVE_DOC_INFO OR MGL_HAVE_DOC_PDF_RU OR MGL_HAVE_DOC_PDF_EN )
+               add_subdirectory( texinfo )
+       endif(MGL_HAVE_DOC_HTML OR MGL_HAVE_DOC_SITE OR MGL_HAVE_DOC_INFO OR MGL_HAVE_DOC_PDF_RU OR MGL_HAVE_DOC_PDF_EN )
 
 endif(NOT MSVC AND NOT BORLAND)
index efab471b57199f21a4e0d682d98bc74f97e180fb..0cd008436d79c8f1e3bb0b2b7086b797d207dfe2 100644 (file)
@@ -1,11 +1,11 @@
-2.2.2 Released ?? February 2014
+2.2.2 Released 10 March 2014
 
 * Add mgl_region_3d() to draw region (or ribbon) between 2 curves. Correspondingly extend mglGraph::Region() function and MGL command 'region'.
-* Add mgl_datac_diffr() for calculation of one step of diffraction by finite-difference method
-* Improve export to TeX
-* Add missing functions to Fortran interface
-* Bugfix for legend with enabled lighting
-* Minor bugfixes and memory leaks
+* Allow LGPL for MathGL widgets.
+* Improve export to TeX.
+* Add missing functions to Fortran interface.
+* Bugfix for legend with enabled lighting.
+* Minor bugfixes and memory leaks.
 
 
 2.2.1 Released 22 January 2014
index 993fb5a83e784b69b141fedc1ffba1044c450abe..52cce1d6af23b0f88fe79d0b2b48afc691eefe7c 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *\r
  ***************************************************************************/\r
 #include "mgl2/fltk.h"\r
+//-----------------------------------------------------------------------------\r
 #if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
 #include <windows.h>\r
 #else\r
 #include <unistd.h>\r
 #endif\r
+void long_calculations()       // just delay which correspond to simulate calculations\r
+{\r
+#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
+       Sleep(1000);\r
+#else\r
+       sleep(1);       // which can be very long\r
+#endif\r
+}\r
 //-----------------------------------------------------------------------------\r
-int test_wnd(mglGraph *gr);\r
-int sample(mglGraph *gr);\r
-int sample_1(mglGraph *gr);\r
-int sample_2(mglGraph *gr);\r
-int sample_3(mglGraph *gr);\r
-int sample_d(mglGraph *gr);\r
-//-----------------------------------------------------------------------------\r
-mglPoint pnt;  // some global variable for changeable data\r
-void *mgl_fltk_tmp(void *)     {       mgl_fltk_run(); return 0;       }\r
-//#define PTHREAD_SAMPLE\r
-//-----------------------------------------------------------------------------\r
+#if defined(PTHREAD_SAMPLE1)   // first variant of multi-threading usage of mglFLTK window\r
+mglFLTK *gr=NULL;\r
+void *calc(void *)\r
+{\r
+       mglPoint pnt;\r
+       for(int i=0;i<10;i++)           // do calculation\r
+       {\r
+               long_calculations();    // which can be very long\r
+               pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);\r
+               if(gr)\r
+               {\r
+                       gr->Clf();                      // make new drawing\r
+                       gr->Line(mglPoint(),pnt,"Ar2");\r
+                       char str[10] = "i=0";   str[2] = '0'+i;\r
+                       gr->Puts(mglPoint(),str);\r
+                       gr->Update();           // update window\r
+               }\r
+       }\r
+       exit(0);\r
+}\r
+int main(int argc,char **argv)\r
+{\r
+       static pthread_t thr;\r
+       pthread_create(&thr,0,calc,0);\r
+       pthread_detach(thr);\r
+       gr = new mglFLTK;\r
+       gr->Run();      return 0;\r
+}\r
+#elif defined(PTHREAD_SAMPLE2) // another variant of multi-threading usage of mglFLTK window. Work only if pthread was enabled for MathGL\r
+mglPoint pnt;  // some global variable for changeable data\r
 int main(int argc,char **argv)\r
 {\r
-#ifdef PTHREAD_SAMPLE\r
        mglFLTK gr("test");\r
-       gr.RunThr();\r
+       gr.RunThr();    // <-- need MathGL version which use pthread\r
        for(int i=0;i<10;i++)   // do calculation\r
        {\r
-#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
-               Sleep(1000);\r
-#else\r
-               sleep(1);           // which can be very long\r
-#endif\r
+               long_calculations();// which can be very long\r
                pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);\r
                gr.Clf();                       // make new drawing\r
                gr.Line(mglPoint(),pnt,"Ar2");\r
                char str[10] = "i=0";   str[3] = '0'+i;\r
-               gr.Puts(mglPoint(),"");\r
                gr.Update();            // update window\r
        }\r
        return 0;       // finish calculations and close the window\r
-#else\r
+}\r
+#else          // just default samples\r
+int test_wnd(mglGraph *gr);\r
+int sample(mglGraph *gr);\r
+int sample_1(mglGraph *gr);\r
+int sample_2(mglGraph *gr);\r
+int sample_3(mglGraph *gr);\r
+int sample_d(mglGraph *gr);\r
+//-----------------------------------------------------------------------------\r
+int main(int argc,char **argv)\r
+{\r
        mglFLTK *gr;\r
        char key = 0;\r
        if(argc>1)      key = argv[1][0]!='-' ? argv[1][0]:argv[1][1];\r
@@ -70,6 +102,5 @@ int main(int argc,char **argv)
                default:        gr = new mglFLTK(sample,"Drop and waves");      break;\r
        }\r
        gr->Run();      return 0;\r
-#endif\r
 }\r
-//-----------------------------------------------------------------------------\r
+#endif\r
index 2032ad7842bf59edf608392abd1952ac42b1dd69..3d34d845d1b3308dc98006da2688a72a80abe988 100644 (file)
@@ -69,6 +69,16 @@ void smgl_surf(mglGraph *gr);
 #include <mgl2/font.h>\r
 void test(mglGraph *gr)\r
 {\r
+       mglData y(50);\r
+       y.Modify("sin(10*x) + 10");\r
+       gr->SetRange('y', y);\r
+       gr->Box();\r
+       gr->Axis();\r
+       gr->Plot(y);\r
+       y.Save("test.dat");\r
+       return;\r
+\r
+\r
        mglParse par;\r
        setlocale(LC_CTYPE, "");\r
        par.Execute(gr,"new x 50 40 '0.8*sin(pi*x)*sin(pi*(y+1)/2)'\n\\r
index 35d1a089eec98b7d2c93941ed5ac61afa854f79c..4018362e6d4e58f0597d911e53d270db2359295e 100644 (file)
  ***************************************************************************/\r
 #include "mgl2/glut.h"\r
 //-----------------------------------------------------------------------------\r
+#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
+#include <windows.h>\r
+#else\r
+#include <unistd.h>\r
+#endif\r
+void long_calculations()       // just delay which correspond to simulate calculations\r
+{\r
+#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
+       Sleep(1000);\r
+#else\r
+       sleep(1);       // which can be very long\r
+#endif\r
+}\r
+//-----------------------------------------------------------------------------\r
+#if defined(PTHREAD_SAMPLE)\r
+mglGLUT *gr=NULL;\r
+void *calc(void *)\r
+{\r
+       mglPoint pnt;\r
+       for(int i=0;i<10;i++)   // do calculation\r
+       {\r
+               sleep(1);           // which can be very long\r
+               pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);\r
+printf("i=%d, gr=%p\n",i,gr);  fflush(stdout);\r
+               if(gr)\r
+               {\r
+                       gr->Clf();                      // make new drawing\r
+                       gr->Line(mglPoint(),pnt,"Ar2");\r
+                       char str[10] = "i=0";   str[2] = '0'+i;\r
+                       gr->Puts(mglPoint(),str);\r
+                       gr->Update();           // update window\r
+               }\r
+       }\r
+       exit(0);\r
+}\r
+int main(int argc,char **argv)\r
+{\r
+       static pthread_t thr;\r
+       pthread_create(&thr,0,calc,0);\r
+       pthread_detach(thr);\r
+       gr = new mglGLUT;\r
+printf("Create gr=%p\n",gr);   fflush(stdout);\r
+       gr->Run();      return 0;\r
+}\r
+#else\r
 int test_wnd(mglGraph *gr);\r
 int sample(mglGraph *gr);\r
 int sample_m(mglGraph *gr);\r
@@ -33,19 +78,16 @@ int main(int argc,char **argv)
        char key = 0;\r
        if(argc>1)      key = argv[1][0]!='-' ? argv[1][0] : argv[1][1];\r
        else    printf("You may specify argument '1', '2', '3' or 'd' for viewing examples of 1d, 2d, 3d or dual plotting\n");\r
-\r
-       const char *desc;\r
-       draw_func func;\r
+       mglGLUT *gr;\r
        switch(key)\r
        {\r
-       case '1':       func = sample_1;        desc = "1D plots";      break;\r
-       case '2':       func = sample_2;        desc = "2D plots";      break;\r
-       case '3':       func = sample_3;        desc = "3D plots";      break;\r
-       case 'd':       func = sample_d;        desc = "Dual plots";    break;\r
-       case 't':       func = test_wnd;        desc = "Testing";       break;\r
-       default:        func = sample;  desc = "Example of molecules";  break;\r
+       case '1':       gr = new mglGLUT(sample_1, "1D plots"); break;\r
+       case '2':       gr = new mglGLUT(sample_2, "2D plots"); break;\r
+       case '3':       gr = new mglGLUT(sample_3, "3D plots"); break;\r
+       case 'd':       gr = new mglGLUT(sample_d, "Dual plots");       break;\r
+       case 't':       gr = new mglGLUT(test_wnd, "Testing");  break;\r
+       default:        gr = new mglGLUT(sample, "Example of molecules");       break;\r
        }\r
-       mglGLUT gr(func,desc);\r
        return 0;\r
 }\r
-//-----------------------------------------------------------------------------\r
+#endif
\ No newline at end of file
index 5a10eb985a78e9aef88201693e4cf84f0b1d5c9d..54cfbcfa690ed35ed448431f7888250ed6183a99 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *\r
  ***************************************************************************/\r
 #include "mgl2/qt.h"\r
+//-----------------------------------------------------------------------------\r
 #if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
 #include <windows.h>\r
 #else\r
 #include <unistd.h>\r
 #endif\r
+void long_calculations()       // just delay which correspond to simulate calculations\r
+{\r
+#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
+       Sleep(1000);\r
+#else\r
+       sleep(1);           // which can be very long\r
+#endif\r
+}\r
 //-----------------------------------------------------------------------------\r
-int test_wnd(mglGraph *gr);\r
-int sample(mglGraph *gr);\r
-int sample_1(mglGraph *gr);\r
-int sample_2(mglGraph *gr);\r
-int sample_3(mglGraph *gr);\r
-int sample_d(mglGraph *gr);\r
-//-----------------------------------------------------------------------------\r
-//#define PTHREAD_SAMPLE\r
-mglPoint pnt;  // some global variable for changeable data\r
-void *mgl_qt_tmp(void *);\r
-//-----------------------------------------------------------------------------\r
+#if defined(PTHREAD_SAMPLE1)   // first variant of multi-threading usage of mglQT window\r
+mglQT *gr=NULL;\r
+void *calc(void *)\r
+{\r
+       mglPoint pnt;\r
+       for(int i=0;i<10;i++)   // do calculation\r
+       {\r
+               long_calculations();       // which can be very long\r
+               pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);\r
+               if(gr)\r
+               {\r
+                       gr->Clf();                      // make new drawing\r
+                       gr->Line(mglPoint(),pnt,"Ar2");\r
+                       char str[10] = "i=0";   str[2] = '0'+i;\r
+                       gr->Puts(mglPoint(),str);\r
+                       gr->Update();           // update window\r
+               }\r
+       }\r
+       exit(0);\r
+}\r
+int main(int argc,char **argv)\r
+{\r
+       static pthread_t thr;\r
+       pthread_create(&thr,0,calc,0);\r
+       pthread_detach(thr);\r
+       gr = new mglQT;\r
+       gr->Run();      return 0;\r
+}\r
+#elif defined(PTHREAD_SAMPLE2) // another variant of multi-threading usage of mglQT window. Work only if pthread was enabled for MathGL\r
 class Foo : public mglDraw\r
 {\r
        mglPoint pnt;  // some result of calculation\r
@@ -43,36 +70,39 @@ public:
        int Draw(mglGraph *gr);\r
        void Calc();\r
 };\r
-//-----------------------------------------------------\r
 void Foo::Calc()\r
 {\r
-       for(int i=0;i<30;i++)   // do calculation\r
+       for(int i=0;i<30;i++)           // do calculation\r
        {\r
-#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__)\r
-               Sleep(1000);\r
-#else\r
-               sleep(1);           // which can be very long\r
-#endif\r
+               long_calculations();    // which can be very long\r
                pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);\r
-               Gr->Update();        // update window\r
+               Gr->Update();                   // update window\r
        }\r
 }\r
-//-----------------------------------------------------\r
 int Foo::Draw(mglGraph *gr)\r
 {\r
        gr->Line(mglPoint(),pnt,"Ar2");\r
        gr->Box();\r
        return 0;\r
 }\r
-//-----------------------------------------------------\r
 int main(int argc,char **argv)\r
 {\r
-#ifdef PTHREAD_SAMPLE\r
        Foo *foo = new Foo;\r
        mglQT gr(foo,"MathGL examples");\r
-       foo->Gr = &gr;   foo->Run();\r
+       foo->Gr = &gr;\r
+       foo->Run();     // <-- need MathGL version which use pthread\r
        return gr.Run();\r
-#else\r
+}\r
+#else          // just default samples\r
+int test_wnd(mglGraph *gr);\r
+int sample(mglGraph *gr);\r
+int sample_1(mglGraph *gr);\r
+int sample_2(mglGraph *gr);\r
+int sample_3(mglGraph *gr);\r
+int sample_d(mglGraph *gr);\r
+//-----------------------------------------------------------------------------\r
+int main(int argc,char **argv)\r
+{\r
        mglQT *gr;\r
        char key = 0;\r
        if(argc>1)      key = argv[1][0]!='-' ? argv[1][0]:argv[1][1];\r
@@ -87,6 +117,5 @@ int main(int argc,char **argv)
        default:        gr = new mglQT(sample,"Drop and waves");        break;\r
        }\r
        gr->Run();      return 0;\r
-#endif\r
 }\r
-//-----------------------------------------------------------------------------\r
+#endif\r
index 5aafe5e9c9e690887daf16f7e9396bf573046c51..7f30f305edd4fe0e3c9cd617b0bd3010a3cdd221 100644 (file)
@@ -213,14 +213,14 @@ void smgl_fexport(mglGraph *gr)   // test file export
 }
 //-----------------------------------------------------------------------------
 const char *mmgl_refill="new x 10 '0.5+rnd':cumsum x 'x':norm x -1 1\n"
-"copy y sin(pi*x)/2\ntitle 'Refill sample'\nbox:axis:plot x y 'o '\n"
+"copy y sin(pi*x)/2\nsubplot 1 1 0 '<_':title 'Refill sample'\nbox:axis:plot x y 'o '\n"
 "new r 100:refill r x y\nplot r 'r'\nfplot 'sin(pi*x)/2' 'B:'";
 void smgl_refill(mglGraph *gr)
 {
        mglData x(10), y(10), r(100);
        x.Modify("0.5+rnd");    x.CumSum("x");  x.Norm(-1,1);
        y.Modify("sin(pi*v)/2",x);
-       if(big!=3)      gr->Title("Refill sample");
+       if(big!=3)      {       gr->SubPlot(1,1,0,"<_");        gr->Title("Refill sample");     }
        gr->Axis();     gr->Box();      gr->Plot(x,y,"o ");
        gr->Refill(r,x,y);      // or you can use r.Refill(x,y,-1,1);
        gr->Plot(r,"r");        gr->FPlot("sin(pi*x)/2","B:");
index 44c3934ab25ebb1ece44c341334092b88711327c..81aec448b846d1e87a5ac1049a01e5c359b574a7 100644 (file)
@@ -445,8 +445,6 @@ public:
 #endif\r
 #if MGL_NO_DATA_A\r
        inline long GetNN() const {     return nx*ny*nz;        }\r
-#else\r
-protected:\r
 #endif\r
        /// Get the value in given cell of the data without border checking\r
        inline mreal v(long i,long j=0,long k=0) const\r
index c3d94e9d13eef0594844662828807e64a35f88a1..14218d9d9ca4c5e6830328057b806955d32aa1ee 100644 (file)
@@ -367,11 +367,7 @@ public:
        /// Direct access to the data cell\r
        inline dual &operator[](long i) {       return a[i];    }\r
 #endif\r
-#if MGL_NO_DATA_A\r
-       inline long GetNN() const {     return nx*ny*nz;        }\r
-#else\r
-protected:\r
-#endif\r
+\r
        /// Get the value in given cell of the data without border checking\r
        inline mreal v(long i,long j=0,long k=0) const\r
 #ifdef DEBUG\r
index 3a6d4395a482b48ae0c03482bcceb0feec79409b..c9bd1bb7d84a5080e2a7a3b0ad4eb00303457779 100644 (file)
@@ -93,10 +93,13 @@ typedef unsigned long uintptr_t;
 \r
 #if defined(_MSC_VER) || defined(__BORLANDC__)\r
 #include <float.h>\r
+#include <math.h>\r
 \r
 const unsigned long mgl_nan[2] = {0xffffffff, 0x7fffffff};\r
 #define NANd   (*(double*)mgl_nan)\r
 #define NANf   (*(float*)&(mgl_nan[1]))\r
+\r
+#if !defined(NAN)\r
 #if MGL_USE_DOUBLE\r
 #define NAN            NANd\r
 #else\r
@@ -104,6 +107,8 @@ const unsigned long mgl_nan[2] = {0xffffffff, 0x7fffffff};
 #endif\r
 #endif\r
 \r
+#endif\r
+\r
 #ifndef M_PI\r
 #define M_PI   3.14159265358979323846  /* pi */\r
 #endif\r
index 6e360d500c7b702f02ad80e5b10997b891c378ac..6d6f295faf7fb4136d0a56448654f6a17cb7aed5 100644 (file)
@@ -75,6 +75,7 @@ public:
        {       mgl_glut_prev_frame(gr);        }\r
        inline void Animation()         ///< Run slideshow (animation) of frames\r
        {       mgl_glut_animation(gr); }\r
+       inline int Run() {};            ///< Run main loop for event handling (placed for similarity to mglWnd)\r
 };\r
 //-----------------------------------------------------------------------------\r
 #endif\r
index 7802ff00e9c3287a4465f2756db664e78fb17950..2bcda48416b34d3db3f64abe26f679c0537e71bb 100644 (file)
@@ -2,7 +2,7 @@
 
 <html>
 <head>
-  <script type="text/javascript" src="sylvester.js"></script>
+  <script type="text/javascript" src="sylvester.src.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>
diff --git a/json/sylvester.js b/json/sylvester.js
deleted file mode 100644 (file)
index 3e83bee..0000000
+++ /dev/null
@@ -1 +0,0 @@
-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/json/sylvester.src.js b/json/sylvester.src.js
new file mode 100644 (file)
index 0000000..97a6491
--- /dev/null
@@ -0,0 +1,1254 @@
+// === Sylvester ===\r
+// Vector and Matrix mathematics modules for JavaScript\r
+// Copyright (c) 2007 James Coglan\r
+// \r
+// Permission is hereby granted, free of charge, to any person obtaining\r
+// a copy of this software and associated documentation files (the "Software"),\r
+// to deal in the Software without restriction, including without limitation\r
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+// and/or sell copies of the Software, and to permit persons to whom the\r
+// Software is furnished to do so, subject to the following conditions:\r
+// \r
+// The above copyright notice and this permission notice shall be included\r
+// in all copies or substantial portions of the Software.\r
+// \r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
+// DEALINGS IN THE SOFTWARE.\r
+\r
+var Sylvester = {\r
+  version: '0.1.3',\r
+  precision: 1e-6\r
+};\r
+\r
+function Vector() {}\r
+Vector.prototype = {\r
+\r
+  // Returns element i of the vector\r
+  e: function(i) {\r
+    return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];\r
+  },\r
+\r
+  // Returns the number of elements the vector has\r
+  dimensions: function() {\r
+    return this.elements.length;\r
+  },\r
+\r
+  // Returns the modulus ('length') of the vector\r
+  modulus: function() {\r
+    return Math.sqrt(this.dot(this));\r
+  },\r
+\r
+  // Returns true iff the vector is equal to the argument\r
+  eql: function(vector) {\r
+    var n = this.elements.length;\r
+    var V = vector.elements || vector;\r
+    if (n != V.length) { return false; }\r
+    do {\r
+      if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; }\r
+    } while (--n);\r
+    return true;\r
+  },\r
+\r
+  // Returns a copy of the vector\r
+  dup: function() {\r
+    return Vector.create(this.elements);\r
+  },\r
+\r
+  // Maps the vector to another vector according to the given function\r
+  map: function(fn) {\r
+    var elements = [];\r
+    this.each(function(x, i) {\r
+      elements.push(fn(x, i));\r
+    });\r
+    return Vector.create(elements);\r
+  },\r
+  \r
+  // Calls the iterator for each element of the vector in turn\r
+  each: function(fn) {\r
+    var n = this.elements.length, k = n, i;\r
+    do { i = k - n;\r
+      fn(this.elements[i], i+1);\r
+    } while (--n);\r
+  },\r
+\r
+  // Returns a new vector created by normalizing the receiver\r
+  toUnitVector: function() {\r
+    var r = this.modulus();\r
+    if (r === 0) { return this.dup(); }\r
+    return this.map(function(x) { return x/r; });\r
+  },\r
+\r
+  // Returns the angle between the vector and the argument (also a vector)\r
+  angleFrom: function(vector) {\r
+    var V = vector.elements || vector;\r
+    var n = this.elements.length, k = n, i;\r
+    if (n != V.length) { return null; }\r
+    var dot = 0, mod1 = 0, mod2 = 0;\r
+    // Work things out in parallel to save time\r
+    this.each(function(x, i) {\r
+      dot += x * V[i-1];\r
+      mod1 += x * x;\r
+      mod2 += V[i-1] * V[i-1];\r
+    });\r
+    mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);\r
+    if (mod1*mod2 === 0) { return null; }\r
+    var theta = dot / (mod1*mod2);\r
+    if (theta < -1) { theta = -1; }\r
+    if (theta > 1) { theta = 1; }\r
+    return Math.acos(theta);\r
+  },\r
+\r
+  // Returns true iff the vector is parallel to the argument\r
+  isParallelTo: function(vector) {\r
+    var angle = this.angleFrom(vector);\r
+    return (angle === null) ? null : (angle <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns true iff the vector is antiparallel to the argument\r
+  isAntiparallelTo: function(vector) {\r
+    var angle = this.angleFrom(vector);\r
+    return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns true iff the vector is perpendicular to the argument\r
+  isPerpendicularTo: function(vector) {\r
+    var dot = this.dot(vector);\r
+    return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns the result of adding the argument to the vector\r
+  add: function(vector) {\r
+    var V = vector.elements || vector;\r
+    if (this.elements.length != V.length) { return null; }\r
+    return this.map(function(x, i) { return x + V[i-1]; });\r
+  },\r
+\r
+  // Returns the result of subtracting the argument from the vector\r
+  subtract: function(vector) {\r
+    var V = vector.elements || vector;\r
+    if (this.elements.length != V.length) { return null; }\r
+    return this.map(function(x, i) { return x - V[i-1]; });\r
+  },\r
+\r
+  // Returns the result of multiplying the elements of the vector by the argument\r
+  multiply: function(k) {\r
+    return this.map(function(x) { return x*k; });\r
+  },\r
+\r
+  x: function(k) { return this.multiply(k); },\r
+\r
+  // Returns the scalar product of the vector with the argument\r
+  // Both vectors must have equal dimensionality\r
+  dot: function(vector) {\r
+    var V = vector.elements || vector;\r
+    var i, product = 0, n = this.elements.length;\r
+    if (n != V.length) { return null; }\r
+    do { product += this.elements[n-1] * V[n-1]; } while (--n);\r
+    return product;\r
+  },\r
+\r
+  // Returns the vector product of the vector with the argument\r
+  // Both vectors must have dimensionality 3\r
+  cross: function(vector) {\r
+    var B = vector.elements || vector;\r
+    if (this.elements.length != 3 || B.length != 3) { return null; }\r
+    var A = this.elements;\r
+    return Vector.create([\r
+      (A[1] * B[2]) - (A[2] * B[1]),\r
+      (A[2] * B[0]) - (A[0] * B[2]),\r
+      (A[0] * B[1]) - (A[1] * B[0])\r
+    ]);\r
+  },\r
+\r
+  // Returns the (absolute) largest element of the vector\r
+  max: function() {\r
+    var m = 0, n = this.elements.length, k = n, i;\r
+    do { i = k - n;\r
+      if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; }\r
+    } while (--n);\r
+    return m;\r
+  },\r
+\r
+  // Returns the index of the first match found\r
+  indexOf: function(x) {\r
+    var index = null, n = this.elements.length, k = n, i;\r
+    do { i = k - n;\r
+      if (index === null && this.elements[i] == x) {\r
+        index = i + 1;\r
+      }\r
+    } while (--n);\r
+    return index;\r
+  },\r
+\r
+  // Returns a diagonal matrix with the vector's elements as its diagonal elements\r
+  toDiagonalMatrix: function() {\r
+    return Matrix.Diagonal(this.elements);\r
+  },\r
+\r
+  // Returns the result of rounding the elements of the vector\r
+  round: function() {\r
+    return this.map(function(x) { return Math.round(x); });\r
+  },\r
+\r
+  // Returns a copy of the vector with elements set to the given value if they\r
+  // differ from it by less than Sylvester.precision\r
+  snapTo: function(x) {\r
+    return this.map(function(y) {\r
+      return (Math.abs(y - x) <= Sylvester.precision) ? x : y;\r
+    });\r
+  },\r
+\r
+  // Returns the vector's distance from the argument, when considered as a point in space\r
+  distanceFrom: function(obj) {\r
+    if (obj.anchor) { return obj.distanceFrom(this); }\r
+    var V = obj.elements || obj;\r
+    if (V.length != this.elements.length) { return null; }\r
+    var sum = 0, part;\r
+    this.each(function(x, i) {\r
+      part = x - V[i-1];\r
+      sum += part * part;\r
+    });\r
+    return Math.sqrt(sum);\r
+  },\r
+\r
+  // Returns true if the vector is point on the given line\r
+  liesOn: function(line) {\r
+    return line.contains(this);\r
+  },\r
+\r
+  // Return true iff the vector is a point in the given plane\r
+  liesIn: function(plane) {\r
+    return plane.contains(this);\r
+  },\r
+\r
+  // Rotates the vector about the given object. The object should be a \r
+  // point if the vector is 2D, and a line if it is 3D. Be careful with line directions!\r
+  rotate: function(t, obj) {\r
+    var V, R, x, y, z;\r
+    switch (this.elements.length) {\r
+      case 2:\r
+        V = obj.elements || obj;\r
+        if (V.length != 2) { return null; }\r
+        R = Matrix.Rotation(t).elements;\r
+        x = this.elements[0] - V[0];\r
+        y = this.elements[1] - V[1];\r
+        return Vector.create([\r
+          V[0] + R[0][0] * x + R[0][1] * y,\r
+          V[1] + R[1][0] * x + R[1][1] * y\r
+        ]);\r
+        break;\r
+      case 3:\r
+        if (!obj.direction) { return null; }\r
+        var C = obj.pointClosestTo(this).elements;\r
+        R = Matrix.Rotation(t, obj.direction).elements;\r
+        x = this.elements[0] - C[0];\r
+        y = this.elements[1] - C[1];\r
+        z = this.elements[2] - C[2];\r
+        return Vector.create([\r
+          C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,\r
+          C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,\r
+          C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z\r
+        ]);\r
+        break;\r
+      default:\r
+        return null;\r
+    }\r
+  },\r
+\r
+  // Returns the result of reflecting the point in the given point, line or plane\r
+  reflectionIn: function(obj) {\r
+    if (obj.anchor) {\r
+      // obj is a plane or line\r
+      var P = this.elements.slice();\r
+      var C = obj.pointClosestTo(P).elements;\r
+      return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);\r
+    } else {\r
+      // obj is a point\r
+      var Q = obj.elements || obj;\r
+      if (this.elements.length != Q.length) { return null; }\r
+      return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });\r
+    }\r
+  },\r
+\r
+  // Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added\r
+  to3D: function() {\r
+    var V = this.dup();\r
+    switch (V.elements.length) {\r
+      case 3: break;\r
+      case 2: V.elements.push(0); break;\r
+      default: return null;\r
+    }\r
+    return V;\r
+  },\r
+\r
+  // Returns a string representation of the vector\r
+  inspect: function() {\r
+    return '[' + this.elements.join(', ') + ']';\r
+  },\r
+\r
+  // Set vector's elements from an array\r
+  setElements: function(els) {\r
+    this.elements = (els.elements || els).slice();\r
+    return this;\r
+  }\r
+};\r
+  \r
+// Constructor function\r
+Vector.create = function(elements) {\r
+  var V = new Vector();\r
+  return V.setElements(elements);\r
+};\r
+\r
+// i, j, k unit vectors\r
+Vector.i = Vector.create([1,0,0]);\r
+Vector.j = Vector.create([0,1,0]);\r
+Vector.k = Vector.create([0,0,1]);\r
+\r
+// Random vector of size n\r
+Vector.Random = function(n) {\r
+  var elements = [];\r
+  do { elements.push(Math.random());\r
+  } while (--n);\r
+  return Vector.create(elements);\r
+};\r
+\r
+// Vector filled with zeros\r
+Vector.Zero = function(n) {\r
+  var elements = [];\r
+  do { elements.push(0);\r
+  } while (--n);\r
+  return Vector.create(elements);\r
+};\r
+\r
+\r
+\r
+function Matrix() {}\r
+Matrix.prototype = {\r
+\r
+  // Returns element (i,j) of the matrix\r
+  e: function(i,j) {\r
+    if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }\r
+    return this.elements[i-1][j-1];\r
+  },\r
+\r
+  // Returns row k of the matrix as a vector\r
+  row: function(i) {\r
+    if (i > this.elements.length) { return null; }\r
+    return Vector.create(this.elements[i-1]);\r
+  },\r
+\r
+  // Returns column k of the matrix as a vector\r
+  col: function(j) {\r
+    if (j > this.elements[0].length) { return null; }\r
+    var col = [], n = this.elements.length, k = n, i;\r
+    do { i = k - n;\r
+      col.push(this.elements[i][j-1]);\r
+    } while (--n);\r
+    return Vector.create(col);\r
+  },\r
+\r
+  // Returns the number of rows/columns the matrix has\r
+  dimensions: function() {\r
+    return {rows: this.elements.length, cols: this.elements[0].length};\r
+  },\r
+\r
+  // Returns the number of rows in the matrix\r
+  rows: function() {\r
+    return this.elements.length;\r
+  },\r
+\r
+  // Returns the number of columns in the matrix\r
+  cols: function() {\r
+    return this.elements[0].length;\r
+  },\r
+\r
+  // Returns true iff the matrix is equal to the argument. You can supply\r
+  // a vector as the argument, in which case the receiver must be a\r
+  // one-column matrix equal to the vector.\r
+  eql: function(matrix) {\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    if (this.elements.length != M.length ||\r
+        this.elements[0].length != M[0].length) { return false; }\r
+    var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;\r
+    do { i = ki - ni;\r
+      nj = kj;\r
+      do { j = kj - nj;\r
+        if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; }\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return true;\r
+  },\r
+\r
+  // Returns a copy of the matrix\r
+  dup: function() {\r
+    return Matrix.create(this.elements);\r
+  },\r
+\r
+  // Maps the matrix to another matrix (of the same dimensions) according to the given function\r
+  map: function(fn) {\r
+    var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;\r
+    do { i = ki - ni;\r
+      nj = kj;\r
+      els[i] = [];\r
+      do { j = kj - nj;\r
+        els[i][j] = fn(this.elements[i][j], i + 1, j + 1);\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return Matrix.create(els);\r
+  },\r
+\r
+  // Returns true iff the argument has the same dimensions as the matrix\r
+  isSameSizeAs: function(matrix) {\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    return (this.elements.length == M.length &&\r
+        this.elements[0].length == M[0].length);\r
+  },\r
+\r
+  // Returns the result of adding the argument to the matrix\r
+  add: function(matrix) {\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    if (!this.isSameSizeAs(M)) { return null; }\r
+    return this.map(function(x, i, j) { return x + M[i-1][j-1]; });\r
+  },\r
+\r
+  // Returns the result of subtracting the argument from the matrix\r
+  subtract: function(matrix) {\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    if (!this.isSameSizeAs(M)) { return null; }\r
+    return this.map(function(x, i, j) { return x - M[i-1][j-1]; });\r
+  },\r
+\r
+  // Returns true iff the matrix can multiply the argument from the left\r
+  canMultiplyFromLeft: function(matrix) {\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    // this.columns should equal matrix.rows\r
+    return (this.elements[0].length == M.length);\r
+  },\r
+\r
+  // Returns the result of multiplying the matrix from the right by the argument.\r
+  // If the argument is a scalar then just multiply all the elements. If the argument is\r
+  // a vector, a vector is returned, which saves you having to remember calling\r
+  // col(1) on the result.\r
+  multiply: function(matrix) {\r
+    if (!matrix.elements) {\r
+      return this.map(function(x) { return x * matrix; });\r
+    }\r
+    var returnVector = matrix.modulus ? true : false;\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    if (!this.canMultiplyFromLeft(M)) { return null; }\r
+    var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;\r
+    var cols = this.elements[0].length, elements = [], sum, nc, c;\r
+    do { i = ki - ni;\r
+      elements[i] = [];\r
+      nj = kj;\r
+      do { j = kj - nj;\r
+        sum = 0;\r
+        nc = cols;\r
+        do { c = cols - nc;\r
+          sum += this.elements[i][c] * M[c][j];\r
+        } while (--nc);\r
+        elements[i][j] = sum;\r
+      } while (--nj);\r
+    } while (--ni);\r
+    var M = Matrix.create(elements);\r
+    return returnVector ? M.col(1) : M;\r
+  },\r
+\r
+  x: function(matrix) { return this.multiply(matrix); },\r
+\r
+  // Returns a submatrix taken from the matrix\r
+  // Argument order is: start row, start col, nrows, ncols\r
+  // Element selection wraps if the required index is outside the matrix's bounds, so you could\r
+  // use this to perform row/column cycling or copy-augmenting.\r
+  minor: function(a, b, c, d) {\r
+    var elements = [], ni = c, i, nj, j;\r
+    var rows = this.elements.length, cols = this.elements[0].length;\r
+    do { i = c - ni;\r
+      elements[i] = [];\r
+      nj = d;\r
+      do { j = d - nj;\r
+        elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols];\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return Matrix.create(elements);\r
+  },\r
+\r
+  // Returns the transpose of the matrix\r
+  transpose: function() {\r
+    var rows = this.elements.length, cols = this.elements[0].length;\r
+    var elements = [], ni = cols, i, nj, j;\r
+    do { i = cols - ni;\r
+      elements[i] = [];\r
+      nj = rows;\r
+      do { j = rows - nj;\r
+        elements[i][j] = this.elements[j][i];\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return Matrix.create(elements);\r
+  },\r
+\r
+  // Returns true iff the matrix is square\r
+  isSquare: function() {\r
+    return (this.elements.length == this.elements[0].length);\r
+  },\r
+\r
+  // Returns the (absolute) largest element of the matrix\r
+  max: function() {\r
+    var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;\r
+    do { i = ki - ni;\r
+      nj = kj;\r
+      do { j = kj - nj;\r
+        if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; }\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return m;\r
+  },\r
+\r
+  // Returns the indeces of the first match found by reading row-by-row from left to right\r
+  indexOf: function(x) {\r
+    var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;\r
+    do { i = ki - ni;\r
+      nj = kj;\r
+      do { j = kj - nj;\r
+        if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; }\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return null;\r
+  },\r
+\r
+  // If the matrix is square, returns the diagonal elements as a vector.\r
+  // Otherwise, returns null.\r
+  diagonal: function() {\r
+    if (!this.isSquare) { return null; }\r
+    var els = [], n = this.elements.length, k = n, i;\r
+    do { i = k - n;\r
+      els.push(this.elements[i][i]);\r
+    } while (--n);\r
+    return Vector.create(els);\r
+  },\r
+\r
+  // Make the matrix upper (right) triangular by Gaussian elimination.\r
+  // This method only adds multiples of rows to other rows. No rows are\r
+  // scaled up or switched, and the determinant is preserved.\r
+  toRightTriangular: function() {\r
+    var M = this.dup(), els;\r
+    var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;\r
+    do { i = k - n;\r
+      if (M.elements[i][i] == 0) {\r
+        for (j = i + 1; j < k; j++) {\r
+          if (M.elements[j][i] != 0) {\r
+            els = []; np = kp;\r
+            do { p = kp - np;\r
+              els.push(M.elements[i][p] + M.elements[j][p]);\r
+            } while (--np);\r
+            M.elements[i] = els;\r
+            break;\r
+          }\r
+        }\r
+      }\r
+      if (M.elements[i][i] != 0) {\r
+        for (j = i + 1; j < k; j++) {\r
+          var multiplier = M.elements[j][i] / M.elements[i][i];\r
+          els = []; np = kp;\r
+          do { p = kp - np;\r
+            // Elements with column numbers up to an including the number\r
+            // of the row that we're subtracting can safely be set straight to\r
+            // zero, since that's the point of this routine and it avoids having\r
+            // to loop over and correct rounding errors later\r
+            els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);\r
+          } while (--np);\r
+          M.elements[j] = els;\r
+        }\r
+      }\r
+    } while (--n);\r
+    return M;\r
+  },\r
+\r
+  toUpperTriangular: function() { return this.toRightTriangular(); },\r
+\r
+  // Returns the determinant for square matrices\r
+  determinant: function() {\r
+    if (!this.isSquare()) { return null; }\r
+    var M = this.toRightTriangular();\r
+    var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;\r
+    do { i = k - n + 1;\r
+      det = det * M.elements[i][i];\r
+    } while (--n);\r
+    return det;\r
+  },\r
+\r
+  det: function() { return this.determinant(); },\r
+\r
+  // Returns true iff the matrix is singular\r
+  isSingular: function() {\r
+    return (this.isSquare() && this.determinant() === 0);\r
+  },\r
+\r
+  // Returns the trace for square matrices\r
+  trace: function() {\r
+    if (!this.isSquare()) { return null; }\r
+    var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i;\r
+    do { i = k - n + 1;\r
+      tr += this.elements[i][i];\r
+    } while (--n);\r
+    return tr;\r
+  },\r
+\r
+  tr: function() { return this.trace(); },\r
+\r
+  // Returns the rank of the matrix\r
+  rank: function() {\r
+    var M = this.toRightTriangular(), rank = 0;\r
+    var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;\r
+    do { i = ki - ni;\r
+      nj = kj;\r
+      do { j = kj - nj;\r
+        if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; }\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return rank;\r
+  },\r
+  \r
+  rk: function() { return this.rank(); },\r
+\r
+  // Returns the result of attaching the given argument to the right-hand side of the matrix\r
+  augment: function(matrix) {\r
+    var M = matrix.elements || matrix;\r
+    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }\r
+    var T = this.dup(), cols = T.elements[0].length;\r
+    var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;\r
+    if (ni != M.length) { return null; }\r
+    do { i = ki - ni;\r
+      nj = kj;\r
+      do { j = kj - nj;\r
+        T.elements[i][cols + j] = M[i][j];\r
+      } while (--nj);\r
+    } while (--ni);\r
+    return T;\r
+  },\r
+\r
+  // Returns the inverse (if one exists) using Gauss-Jordan\r
+  inverse: function() {\r
+    if (!this.isSquare() || this.isSingular()) { return null; }\r
+    var ni = this.elements.length, ki = ni, i, j;\r
+    var M = this.augment(Matrix.I(ni)).toRightTriangular();\r
+    var np, kp = M.elements[0].length, p, els, divisor;\r
+    var inverse_elements = [], new_element;\r
+    // Matrix is non-singular so there will be no zeros on the diagonal\r
+    // Cycle through rows from last to first\r
+    do { i = ni - 1;\r
+      // First, normalise diagonal elements to 1\r
+      els = []; np = kp;\r
+      inverse_elements[i] = [];\r
+      divisor = M.elements[i][i];\r
+      do { p = kp - np;\r
+        new_element = M.elements[i][p] / divisor;\r
+        els.push(new_element);\r
+        // Shuffle of the current row of the right hand side into the results\r
+        // array as it will not be modified by later runs through this loop\r
+        if (p >= ki) { inverse_elements[i].push(new_element); }\r
+      } while (--np);\r
+      M.elements[i] = els;\r
+      // Then, subtract this row from those above it to\r
+      // give the identity matrix on the left hand side\r
+      for (j = 0; j < i; j++) {\r
+        els = []; np = kp;\r
+        do { p = kp - np;\r
+          els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);\r
+        } while (--np);\r
+        M.elements[j] = els;\r
+      }\r
+    } while (--ni);\r
+    return Matrix.create(inverse_elements);\r
+  },\r
+\r
+  inv: function() { return this.inverse(); },\r
+\r
+  // Returns the result of rounding all the elements\r
+  round: function() {\r
+    return this.map(function(x) { return Math.round(x); });\r
+  },\r
+\r
+  // Returns a copy of the matrix with elements set to the given value if they\r
+  // differ from it by less than Sylvester.precision\r
+  snapTo: function(x) {\r
+    return this.map(function(p) {\r
+      return (Math.abs(p - x) <= Sylvester.precision) ? x : p;\r
+    });\r
+  },\r
+\r
+  // Returns a string representation of the matrix\r
+  inspect: function() {\r
+    var matrix_rows = [];\r
+    var n = this.elements.length, k = n, i;\r
+    do { i = k - n;\r
+      matrix_rows.push(Vector.create(this.elements[i]).inspect());\r
+    } while (--n);\r
+    return matrix_rows.join('\n');\r
+  },\r
+\r
+  // Set the matrix's elements from an array. If the argument passed\r
+  // is a vector, the resulting matrix will be a single column.\r
+  setElements: function(els) {\r
+    var i, elements = els.elements || els;\r
+    if (typeof(elements[0][0]) != 'undefined') {\r
+      var ni = elements.length, ki = ni, nj, kj, j;\r
+      this.elements = [];\r
+      do { i = ki - ni;\r
+        nj = elements[i].length; kj = nj;\r
+        this.elements[i] = [];\r
+        do { j = kj - nj;\r
+          this.elements[i][j] = elements[i][j];\r
+        } while (--nj);\r
+      } while(--ni);\r
+      return this;\r
+    }\r
+    var n = elements.length, k = n;\r
+    this.elements = [];\r
+    do { i = k - n;\r
+      this.elements.push([elements[i]]);\r
+    } while (--n);\r
+    return this;\r
+  }\r
+};\r
+\r
+// Constructor function\r
+Matrix.create = function(elements) {\r
+  var M = new Matrix();\r
+  return M.setElements(elements);\r
+};\r
+\r
+// Identity matrix of size n\r
+Matrix.I = function(n) {\r
+  var els = [], k = n, i, nj, j;\r
+  do { i = k - n;\r
+    els[i] = []; nj = k;\r
+    do { j = k - nj;\r
+      els[i][j] = (i == j) ? 1 : 0;\r
+    } while (--nj);\r
+  } while (--n);\r
+  return Matrix.create(els);\r
+};\r
+\r
+// Diagonal matrix - all off-diagonal elements are zero\r
+Matrix.Diagonal = function(elements) {\r
+  var n = elements.length, k = n, i;\r
+  var M = Matrix.I(n);\r
+  do { i = k - n;\r
+    M.elements[i][i] = elements[i];\r
+  } while (--n);\r
+  return M;\r
+};\r
+\r
+// Rotation matrix about some axis. If no axis is\r
+// supplied, assume we're after a 2D transform\r
+Matrix.Rotation = function(theta, a) {\r
+  if (!a) {\r
+    return Matrix.create([\r
+      [Math.cos(theta),  -Math.sin(theta)],\r
+      [Math.sin(theta),   Math.cos(theta)]\r
+    ]);\r
+  }\r
+  var axis = a.dup();\r
+  if (axis.elements.length != 3) { return null; }\r
+  var mod = axis.modulus();\r
+  var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod;\r
+  var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c;\r
+  // Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp\r
+  // That proof rotates the co-ordinate system so theta\r
+  // becomes -theta and sin becomes -sin here.\r
+  return Matrix.create([\r
+    [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],\r
+    [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],\r
+    [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ]\r
+  ]);\r
+};\r
+\r
+// Special case rotations\r
+Matrix.RotationX = function(t) {\r
+  var c = Math.cos(t), s = Math.sin(t);\r
+  return Matrix.create([\r
+    [  1,  0,  0 ],\r
+    [  0,  c, -s ],\r
+    [  0,  s,  c ]\r
+  ]);\r
+};\r
+Matrix.RotationY = function(t) {\r
+  var c = Math.cos(t), s = Math.sin(t);\r
+  return Matrix.create([\r
+    [  c,  0,  s ],\r
+    [  0,  1,  0 ],\r
+    [ -s,  0,  c ]\r
+  ]);\r
+};\r
+Matrix.RotationZ = function(t) {\r
+  var c = Math.cos(t), s = Math.sin(t);\r
+  return Matrix.create([\r
+    [  c, -s,  0 ],\r
+    [  s,  c,  0 ],\r
+    [  0,  0,  1 ]\r
+  ]);\r
+};\r
+\r
+// Random matrix of n rows, m columns\r
+Matrix.Random = function(n, m) {\r
+  return Matrix.Zero(n, m).map(\r
+    function() { return Math.random(); }\r
+  );\r
+};\r
+\r
+// Matrix filled with zeros\r
+Matrix.Zero = function(n, m) {\r
+  var els = [], ni = n, i, nj, j;\r
+  do { i = n - ni;\r
+    els[i] = [];\r
+    nj = m;\r
+    do { j = m - nj;\r
+      els[i][j] = 0;\r
+    } while (--nj);\r
+  } while (--ni);\r
+  return Matrix.create(els);\r
+};\r
+\r
+\r
+\r
+function Line() {}\r
+Line.prototype = {\r
+\r
+  // Returns true if the argument occupies the same space as the line\r
+  eql: function(line) {\r
+    return (this.isParallelTo(line) && this.contains(line.anchor));\r
+  },\r
+\r
+  // Returns a copy of the line\r
+  dup: function() {\r
+    return Line.create(this.anchor, this.direction);\r
+  },\r
+\r
+  // Returns the result of translating the line by the given vector/array\r
+  translate: function(vector) {\r
+    var V = vector.elements || vector;\r
+    return Line.create([\r
+      this.anchor.elements[0] + V[0],\r
+      this.anchor.elements[1] + V[1],\r
+      this.anchor.elements[2] + (V[2] || 0)\r
+    ], this.direction);\r
+  },\r
+\r
+  // Returns true if the line is parallel to the argument. Here, 'parallel to'\r
+  // means that the argument's direction is either parallel or antiparallel to\r
+  // the line's own direction. A line is parallel to a plane if the two do not\r
+  // have a unique intersection.\r
+  isParallelTo: function(obj) {\r
+    if (obj.normal) { return obj.isParallelTo(this); }\r
+    var theta = this.direction.angleFrom(obj.direction);\r
+    return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns the line's perpendicular distance from the argument,\r
+  // which can be a point, a line or a plane\r
+  distanceFrom: function(obj) {\r
+    if (obj.normal) { return obj.distanceFrom(this); }\r
+    if (obj.direction) {\r
+      // obj is a line\r
+      if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); }\r
+      var N = this.direction.cross(obj.direction).toUnitVector().elements;\r
+      var A = this.anchor.elements, B = obj.anchor.elements;\r
+      return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);\r
+    } else {\r
+      // obj is a point\r
+      var P = obj.elements || obj;\r
+      var A = this.anchor.elements, D = this.direction.elements;\r
+      var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2];\r
+      var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3);\r
+      if (modPA === 0) return 0;\r
+      // Assumes direction vector is normalized\r
+      var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA;\r
+      var sin2 = 1 - cosTheta*cosTheta;\r
+      return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2));\r
+    }\r
+  },\r
+\r
+  // Returns true iff the argument is a point on the line\r
+  contains: function(point) {\r
+    var dist = this.distanceFrom(point);\r
+    return (dist !== null && dist <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns true iff the line lies in the given plane\r
+  liesIn: function(plane) {\r
+    return plane.contains(this);\r
+  },\r
+\r
+  // Returns true iff the line has a unique point of intersection with the argument\r
+  intersects: function(obj) {\r
+    if (obj.normal) { return obj.intersects(this); }\r
+    return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns the unique intersection point with the argument, if one exists\r
+  intersectionWith: function(obj) {\r
+    if (obj.normal) { return obj.intersectionWith(this); }\r
+    if (!this.intersects(obj)) { return null; }\r
+    var P = this.anchor.elements, X = this.direction.elements,\r
+        Q = obj.anchor.elements, Y = obj.direction.elements;\r
+    var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2];\r
+    var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2];\r
+    var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3;\r
+    var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3;\r
+    var XdotX = X1*X1 + X2*X2 + X3*X3;\r
+    var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3;\r
+    var XdotY = X1*Y1 + X2*Y2 + X3*Y3;\r
+    var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);\r
+    return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]);\r
+  },\r
+\r
+  // Returns the point on the line that is closest to the given point or line\r
+  pointClosestTo: function(obj) {\r
+    if (obj.direction) {\r
+      // obj is a line\r
+      if (this.intersects(obj)) { return this.intersectionWith(obj); }\r
+      if (this.isParallelTo(obj)) { return null; }\r
+      var D = this.direction.elements, E = obj.direction.elements;\r
+      var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2];\r
+      // Create plane containing obj and the shared normal and intersect this with it\r
+      // Thank you: http://www.cgafaq.info/wiki/Line-line_distance\r
+      var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2);\r
+      var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]);\r
+      var P = Plane.create(obj.anchor, N);\r
+      return P.intersectionWith(this);\r
+    } else {\r
+      // obj is a point\r
+      var P = obj.elements || obj;\r
+      if (this.contains(P)) { return Vector.create(P); }\r
+      var A = this.anchor.elements, D = this.direction.elements;\r
+      var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2];\r
+      var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2),\r
+          z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3);\r
+      var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]);\r
+      var k = this.distanceFrom(P) / V.modulus();\r
+      return Vector.create([\r
+        P[0] + V.elements[0] * k,\r
+        P[1] + V.elements[1] * k,\r
+        (P[2] || 0) + V.elements[2] * k\r
+      ]);\r
+    }\r
+  },\r
+\r
+  // Returns a copy of the line rotated by t radians about the given line. Works by\r
+  // finding the argument's closest point to this line's anchor point (call this C) and\r
+  // rotating the anchor about C. Also rotates the line's direction about the argument's.\r
+  // Be careful with this - the rotation axis' direction affects the outcome!\r
+  rotate: function(t, line) {\r
+    // If we're working in 2D\r
+    if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); }\r
+    var R = Matrix.Rotation(t, line.direction).elements;\r
+    var C = line.pointClosestTo(this.anchor).elements;\r
+    var A = this.anchor.elements, D = this.direction.elements;\r
+    var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];\r
+    var x = A1 - C1, y = A2 - C2, z = A3 - C3;\r
+    return Line.create([\r
+      C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,\r
+      C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,\r
+      C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z\r
+    ], [\r
+      R[0][0] * D[0] + R[0][1] * D[1] + R[0][2] * D[2],\r
+      R[1][0] * D[0] + R[1][1] * D[1] + R[1][2] * D[2],\r
+      R[2][0] * D[0] + R[2][1] * D[1] + R[2][2] * D[2]\r
+    ]);\r
+  },\r
+\r
+  // Returns the line's reflection in the given point or line\r
+  reflectionIn: function(obj) {\r
+    if (obj.normal) {\r
+      // obj is a plane\r
+      var A = this.anchor.elements, D = this.direction.elements;\r
+      var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2];\r
+      var newA = this.anchor.reflectionIn(obj).elements;\r
+      // Add the line's direction vector to its anchor, then mirror that in the plane\r
+      var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3;\r
+      var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements;\r
+      var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]];\r
+      return Line.create(newA, newD);\r
+    } else if (obj.direction) {\r
+      // obj is a line - reflection obtained by rotating PI radians about obj\r
+      return this.rotate(Math.PI, obj);\r
+    } else {\r
+      // obj is a point - just reflect the line's anchor in it\r
+      var P = obj.elements || obj;\r
+      return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction);\r
+    }\r
+  },\r
+\r
+  // Set the line's anchor point and direction.\r
+  setVectors: function(anchor, direction) {\r
+    // Need to do this so that line's properties are not\r
+    // references to the arguments passed in\r
+    anchor = Vector.create(anchor);\r
+    direction = Vector.create(direction);\r
+    if (anchor.elements.length == 2) {anchor.elements.push(0); }\r
+    if (direction.elements.length == 2) { direction.elements.push(0); }\r
+    if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; }\r
+    var mod = direction.modulus();\r
+    if (mod === 0) { return null; }\r
+    this.anchor = anchor;\r
+    this.direction = Vector.create([\r
+      direction.elements[0] / mod,\r
+      direction.elements[1] / mod,\r
+      direction.elements[2] / mod\r
+    ]);\r
+    return this;\r
+  }\r
+};\r
+\r
+  \r
+// Constructor function\r
+Line.create = function(anchor, direction) {\r
+  var L = new Line();\r
+  return L.setVectors(anchor, direction);\r
+};\r
+\r
+// Axes\r
+Line.X = Line.create(Vector.Zero(3), Vector.i);\r
+Line.Y = Line.create(Vector.Zero(3), Vector.j);\r
+Line.Z = Line.create(Vector.Zero(3), Vector.k);\r
+\r
+\r
+\r
+function Plane() {}\r
+Plane.prototype = {\r
+\r
+  // Returns true iff the plane occupies the same space as the argument\r
+  eql: function(plane) {\r
+    return (this.contains(plane.anchor) && this.isParallelTo(plane));\r
+  },\r
+\r
+  // Returns a copy of the plane\r
+  dup: function() {\r
+    return Plane.create(this.anchor, this.normal);\r
+  },\r
+\r
+  // Returns the result of translating the plane by the given vector\r
+  translate: function(vector) {\r
+    var V = vector.elements || vector;\r
+    return Plane.create([\r
+      this.anchor.elements[0] + V[0],\r
+      this.anchor.elements[1] + V[1],\r
+      this.anchor.elements[2] + (V[2] || 0)\r
+    ], this.normal);\r
+  },\r
+\r
+  // Returns true iff the plane is parallel to the argument. Will return true\r
+  // if the planes are equal, or if you give a line and it lies in the plane.\r
+  isParallelTo: function(obj) {\r
+    var theta;\r
+    if (obj.normal) {\r
+      // obj is a plane\r
+      theta = this.normal.angleFrom(obj.normal);\r
+      return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision);\r
+    } else if (obj.direction) {\r
+      // obj is a line\r
+      return this.normal.isPerpendicularTo(obj.direction);\r
+    }\r
+    return null;\r
+  },\r
+  \r
+  // Returns true iff the receiver is perpendicular to the argument\r
+  isPerpendicularTo: function(plane) {\r
+    var theta = this.normal.angleFrom(plane.normal);\r
+    return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision);\r
+  },\r
+\r
+  // Returns the plane's distance from the given object (point, line or plane)\r
+  distanceFrom: function(obj) {\r
+    if (this.intersects(obj) || this.contains(obj)) { return 0; }\r
+    if (obj.anchor) {\r
+      // obj is a plane or line\r
+      var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements;\r
+      return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);\r
+    } else {\r
+      // obj is a point\r
+      var P = obj.elements || obj;\r
+      var A = this.anchor.elements, N = this.normal.elements;\r
+      return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]);\r
+    }\r
+  },\r
+\r
+  // Returns true iff the plane contains the given point or line\r
+  contains: function(obj) {\r
+    if (obj.normal) { return null; }\r
+    if (obj.direction) {\r
+      return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction)));\r
+    } else {\r
+      var P = obj.elements || obj;\r
+      var A = this.anchor.elements, N = this.normal.elements;\r
+      var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0)));\r
+      return (diff <= Sylvester.precision);\r
+    }\r
+  },\r
+\r
+  // Returns true iff the plane has a unique point/line of intersection with the argument\r
+  intersects: function(obj) {\r
+    if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; }\r
+    return !this.isParallelTo(obj);\r
+  },\r
+\r
+  // Returns the unique intersection with the argument, if one exists. The result\r
+  // will be a vector if a line is supplied, and a line if a plane is supplied.\r
+  intersectionWith: function(obj) {\r
+    if (!this.intersects(obj)) { return null; }\r
+    if (obj.direction) {\r
+      // obj is a line\r
+      var A = obj.anchor.elements, D = obj.direction.elements,\r
+          P = this.anchor.elements, N = this.normal.elements;\r
+      var multiplier = (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]);\r
+      return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]);\r
+    } else if (obj.normal) {\r
+      // obj is a plane\r
+      var direction = this.normal.cross(obj.normal).toUnitVector();\r
+      // To find an anchor point, we find one co-ordinate that has a value\r
+      // of zero somewhere on the intersection, and remember which one we picked\r
+      var N = this.normal.elements, A = this.anchor.elements,\r
+          O = obj.normal.elements, B = obj.anchor.elements;\r
+      var solver = Matrix.Zero(2,2), i = 0;\r
+      while (solver.isSingular()) {\r
+        i++;\r
+        solver = Matrix.create([\r
+          [ N[i%3], N[(i+1)%3] ],\r
+          [ O[i%3], O[(i+1)%3]  ]\r
+        ]);\r
+      }\r
+      // Then we solve the simultaneous equations in the remaining dimensions\r
+      var inverse = solver.inverse().elements;\r
+      var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2];\r
+      var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2];\r
+      var intersection = [\r
+        inverse[0][0] * x + inverse[0][1] * y,\r
+        inverse[1][0] * x + inverse[1][1] * y\r
+      ];\r
+      var anchor = [];\r
+      for (var j = 1; j <= 3; j++) {\r
+        // This formula picks the right element from intersection by\r
+        // cycling depending on which element we set to zero above\r
+        anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]);\r
+      }\r
+      return Line.create(anchor, direction);\r
+    }\r
+  },\r
+\r
+  // Returns the point in the plane closest to the given point\r
+  pointClosestTo: function(point) {\r
+    var P = point.elements || point;\r
+    var A = this.anchor.elements, N = this.normal.elements;\r
+    var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2];\r
+    return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]);\r
+  },\r
+\r
+  // Returns a copy of the plane, rotated by t radians about the given line\r
+  // See notes on Line#rotate.\r
+  rotate: function(t, line) {\r
+    var R = Matrix.Rotation(t, line.direction).elements;\r
+    var C = line.pointClosestTo(this.anchor).elements;\r
+    var A = this.anchor.elements, N = this.normal.elements;\r
+    var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];\r
+    var x = A1 - C1, y = A2 - C2, z = A3 - C3;\r
+    return Plane.create([\r
+      C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,\r
+      C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,\r
+      C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z\r
+    ], [\r
+      R[0][0] * N[0] + R[0][1] * N[1] + R[0][2] * N[2],\r
+      R[1][0] * N[0] + R[1][1] * N[1] + R[1][2] * N[2],\r
+      R[2][0] * N[0] + R[2][1] * N[1] + R[2][2] * N[2]\r
+    ]);\r
+  },\r
+\r
+  // Returns the reflection of the plane in the given point, line or plane.\r
+  reflectionIn: function(obj) {\r
+    if (obj.normal) {\r
+      // obj is a plane\r
+      var A = this.anchor.elements, N = this.normal.elements;\r
+      var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2];\r
+      var newA = this.anchor.reflectionIn(obj).elements;\r
+      // Add the plane's normal to its anchor, then mirror that in the other plane\r
+      var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3;\r
+      var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements;\r
+      var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]];\r
+      return Plane.create(newA, newN);\r
+    } else if (obj.direction) {\r
+      // obj is a line\r
+      return this.rotate(Math.PI, obj);\r
+    } else {\r
+      // obj is a point\r
+      var P = obj.elements || obj;\r
+      return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal);\r
+    }\r
+  },\r
+\r
+  // Sets the anchor point and normal to the plane. If three arguments are specified,\r
+  // the normal is calculated by assuming the three points should lie in the same plane.\r
+  // If only two are sepcified, the second is taken to be the normal. Normal vector is\r
+  // normalised before storage.\r
+  setVectors: function(anchor, v1, v2) {\r
+    anchor = Vector.create(anchor);\r
+    anchor = anchor.to3D(); if (anchor === null) { return null; }\r
+    v1 = Vector.create(v1);\r
+    v1 = v1.to3D(); if (v1 === null) { return null; }\r
+    if (typeof(v2) == 'undefined') {\r
+      v2 = null;\r
+    } else {\r
+      v2 = Vector.create(v2);\r
+      v2 = v2.to3D(); if (v2 === null) { return null; }\r
+    }\r
+    var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2];\r
+    var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2];\r
+    var normal, mod;\r
+    if (v2 !== null) {\r
+      var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2];\r
+      normal = Vector.create([\r
+        (v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2),\r
+        (v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3),\r
+        (v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1)\r
+      ]);\r
+      mod = normal.modulus();\r
+      if (mod === 0) { return null; }\r
+      normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]);\r
+    } else {\r
+      mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13);\r
+      if (mod === 0) { return null; }\r
+      normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]);\r
+    }\r
+    this.anchor = anchor;\r
+    this.normal = normal;\r
+    return this;\r
+  }\r
+};\r
+\r
+// Constructor function\r
+Plane.create = function(anchor, v1, v2) {\r
+  var P = new Plane();\r
+  return P.setVectors(anchor, v1, v2);\r
+};\r
+\r
+// X-Y-Z planes\r
+Plane.XY = Plane.create(Vector.Zero(3), Vector.k);\r
+Plane.YZ = Plane.create(Vector.Zero(3), Vector.i);\r
+Plane.ZX = Plane.create(Vector.Zero(3), Vector.j);\r
+Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX;\r
+\r
+// Utility functions\r
+var $V = Vector.create;\r
+var $M = Matrix.create;\r
+var $L = Line.create;\r
+var $P = Plane.create;\r
index 7a40b249c20c441eacff4dccdf57cf75121a1aac..c0a81e562a80b154852815202736c36abec0bdde 100644 (file)
@@ -585,7 +585,7 @@ void mglCanvas::DrawLabels(mglAxis &aa, bool inv, const mglMatrix *M)
                kk[i] = AddPnt(M, o+d*aa.txt[i].val,-1,d,0,7);
        }
 
-       for(l=0,c=1e7,i=0;i<n-1;i++)
+       for(l=0,c=INFINITY,i=0;i<n-1;i++)
        {
                // exclude factors
                if(aa.ch!='c' && (aa.txt[i].val<aa.v1 || aa.txt[i+1].val<aa.v1 || aa.txt[i].val>aa.v2 || aa.txt[i+1].val>aa.v2))
@@ -688,7 +688,7 @@ void mglCanvas::DrawGrid(mglAxis &aa)
        mglPoint pp[8]={Min,Min,Min,Min,Max,Max,Max,Max},nan=mglPoint(NAN), oo[8], org=Min;
        pp[1].x=Max.x;  pp[2].y=Max.y;  pp[3].z=Max.z;
        pp[4].x=Min.x;  pp[5].y=Min.y;  pp[6].z=Min.z;
-       mreal zm=1e5;
+       mreal zm=INFINITY;
        memcpy(oo,pp,8*sizeof(mglPoint));
        for(int i=0;i<8;i++)    // find deepest point
        {
@@ -849,7 +849,7 @@ void mglCanvas::Box(const char *col, bool ticks)
                        mglPoint p[8]={Min,Min,Min,Min,Max,Max,Max,Max},nan=mglPoint(NAN),oo[8];
                        p[1].x=Max.x;   p[2].y=Max.y;   p[3].z=Max.z;
                        p[4].x=Min.x;   p[5].y=Min.y;   p[6].z=Min.z;
-                       mreal zm=1e5;   int im=0;
+                       mreal zm=INFINITY;      int im=0;
                        memcpy(oo,p,8*sizeof(mglPoint));
                        for(int i=0;i<8;i++)    // find deepest point
                        {
index df7c74b593439491b1a5f8e58654033fd59cba99..6e9660e7341d40b3c663c568ff5977740ca47007 100644 (file)
@@ -316,7 +316,7 @@ bool mglBase::RecalcCRange()
        {       FMin.c = Min.c; FMax.c = Max.c; }\r
        else\r
        {\r
-               FMin.c = 1e30;  FMax.c = -1e30;\r
+               FMin.c = INFINITY;      FMax.c = -INFINITY;\r
                register int i;\r
                mreal a;\r
                int n=30;\r
@@ -339,8 +339,8 @@ void mglBase::RecalcBorder()
        {       FMin = Min;     FMax = Max;     }\r
        else\r
        {\r
-               FMin = mglPoint( 1e30, 1e30, 1e30);\r
-               FMax = mglPoint(-1e30,-1e30,-1e30);\r
+               FMin = mglPoint( INFINITY, INFINITY, INFINITY);\r
+               FMax = mglPoint(-INFINITY,-INFINITY,-INFINITY);\r
                register int i,j;\r
                int n=30;\r
                for(i=0;i<=n;i++)       for(j=0;j<=n;j++)       // x range\r
index 3481a07821a0e7e0c1f9f8ce939f789d2be29ab3..b886cd8c9ebfcd8dd842842bca01bd6773ba1d16 100644 (file)
@@ -1284,7 +1284,7 @@ void MGL_EXPORT mgl_data_norm_slice(HMDT d, mreal v1,mreal v2,char dir,long keep
 //#pragma omp parallel for private(m1,m2,aa,e,e0)      // TODO add omp comparison here\r
                for(long k=0;k<nz;k++)\r
                {\r
-                       m1 = 1e20;      m2 = -1e20;     e=0;\r
+                       m1 = INFINITY;  m2 = -INFINITY; e=0;\r
                        for(long i=0;i<nx*ny;i++)\r
                        {\r
                                aa = a[i+nx*ny*k];\r
@@ -1305,7 +1305,7 @@ void MGL_EXPORT mgl_data_norm_slice(HMDT d, mreal v1,mreal v2,char dir,long keep
 //#pragma omp parallel for private(m1,m2,aa,e,e0)      // TODO add omp comparison here\r
                for(long j=0;j<ny;j++)\r
                {\r
-                       m1 = 1e20;      m2 = -1e20;     e=0;\r
+                       m1 = INFINITY;  m2 = -INFINITY; e=0;\r
                        for(long k=0;k<nz;k++)  for(long i=0;i<nx;i++)\r
                        {\r
                                aa = a[i+nx*(j+ny*k)];\r
@@ -1327,7 +1327,7 @@ void MGL_EXPORT mgl_data_norm_slice(HMDT d, mreal v1,mreal v2,char dir,long keep
 //#pragma omp parallel for private(m1,m2,aa,e,e0)      // TODO add omp comparison here\r
                for(long i=0;i<nx;i++)\r
                {\r
-                       m1 = 1e20;      m2 = -1e20;     e=0;\r
+                       m1 = INFINITY;  m2 = -INFINITY; e=0;\r
                        for(long k=0;k<ny*nz;k++)\r
                        {\r
                                aa = a[i+nx*k];\r
index 215c924149dbc3ed97abf8ec6c5f762f30317caa..1ac3472466bff9bf55198c8fed2eb7dbcd44dab5 100644 (file)
@@ -458,12 +458,12 @@ int MGL_EXPORT mgl_data_read_mat_(uintptr_t *d, const char *fname,int *dim,int l
 //-----------------------------------------------------------------------------\r
 mreal MGL_EXPORT mgl_data_max(HCDT d)\r
 {\r
-       mreal m1=-1e10;\r
+       mreal m1=-INFINITY;\r
        long nn=d->GetNN();\r
        const mglData *b = dynamic_cast<const mglData *>(d);\r
 #pragma omp parallel\r
        {\r
-               register mreal m=-1e10, v;\r
+               register mreal m=-INFINITY, v;\r
                if(b)\r
 #pragma omp for nowait\r
                        for(long i=0;i<nn;i++)\r
@@ -481,12 +481,12 @@ mreal MGL_EXPORT mgl_data_max_(uintptr_t *d)      {       return mgl_data_max(_DT_);      }
 //-----------------------------------------------------------------------------\r
 mreal MGL_EXPORT mgl_data_min(HCDT d)\r
 {\r
-       mreal m1=0;\r
+       mreal m1=INFINITY;\r
        long nn=d->GetNN();\r
        const mglData *b = dynamic_cast<const mglData *>(d);\r
 #pragma omp parallel\r
        {\r
-               register mreal m=0, v;\r
+               register mreal m=INFINITY, v;\r
                if(b)\r
 #pragma omp for nowait\r
                        for(long i=0;i<nn;i++)\r
@@ -527,12 +527,12 @@ mreal MGL_EXPORT mgl_data_neg_max_(uintptr_t *d)  {       return mgl_data_neg_max(_DT_)
 //-----------------------------------------------------------------------------\r
 mreal MGL_EXPORT mgl_data_pos_min(HCDT d)\r
 {\r
-       mreal m1=1e10;\r
+       mreal m1=INFINITY;\r
        long nn=d->GetNN();\r
        const mglData *b = dynamic_cast<const mglData *>(d);\r
 #pragma omp parallel\r
        {\r
-               register mreal m=1e10, v;\r
+               register mreal m=INFINITY, v;\r
                if(b)\r
 #pragma omp for nowait\r
                        for(long i=0;i<nn;i++)\r
@@ -550,11 +550,11 @@ mreal MGL_EXPORT mgl_data_pos_min_(uintptr_t *d)  {       return mgl_data_pos_min(_DT_)
 //-----------------------------------------------------------------------------\r
 mreal MGL_EXPORT mgl_data_max_int(HCDT d, long *i, long *j, long *k)\r
 {\r
-       mreal m1=-1e10;\r
+       mreal m1=-INFINITY;\r
        long nx=d->GetNx(), ny=d->GetNy(), nn=d->GetNN();\r
 #pragma omp parallel\r
        {\r
-               register mreal m=-1e10, v;\r
+               register mreal m=-INFINITY, v;\r
                long im=-1,jm=-1,km=-1;\r
 #pragma omp for nowait\r
                for(long ii=0;ii<nn;ii++)\r
@@ -574,11 +574,11 @@ mreal MGL_EXPORT mgl_data_max_int_(uintptr_t *d, int *i, int *j, int *k)
 //-----------------------------------------------------------------------------\r
 mreal MGL_EXPORT mgl_data_min_int(HCDT d, long *i, long *j, long *k)\r
 {\r
-       mreal m1=1e10;\r
+       mreal m1=INFINITY;\r
        long nx=d->GetNx(), ny=d->GetNy(), nn=d->GetNN();\r
 #pragma omp parallel\r
        {\r
-               register mreal m=1e10, v;\r
+               register mreal m=INFINITY, v;\r
                long im=-1,jm=-1,km=-1;\r
 #pragma omp for nowait\r
                for(long ii=0;ii<nn;ii++)\r
@@ -704,7 +704,7 @@ void MGL_EXPORT mgl_data_fill_(uintptr_t *d, mreal *x1,mreal *x2,const char *dir
 void MGL_EXPORT mgl_data_norm(HMDT d, mreal v1,mreal v2,long sym,long dim)\r
 {\r
        long s,nn=d->nx*d->ny*d->nz;\r
-       mreal a1=1e20,a2=-1e20,v,*a=d->a;\r
+       mreal a1=INFINITY,a2=-INFINITY,v,*a=d->a;\r
        s = dim*d->ny*(d->nz>1 ? d->nx : 1);\r
        for(long i=s;i<nn;i++)  // determines borders of existing data\r
        {       a1 = a1>a[i] ? a[i]:a1; a2 = a2<a[i] ? a[i]:a2; }\r
index 7bdab9421541f0c40dd5fae46dae5009e5bf87bf..1f907f6a9f9bbb5a99717ba9444a281d722ca38a 100644 (file)
@@ -120,7 +120,7 @@ void MGL_EXPORT mgl_data_export(HCDT dd, const char *fname, const char *scheme,m
        if(ns<0 || ns>=nz)      ns=0;\r
        if(v1==v2)\r
        {\r
-               v1 = 1e20;      v2=-1e20;\r
+               v1 = INFINITY;  v2=-INFINITY;\r
                if(md)\r
 //#pragma omp parallel for     // NOTE comparison here\r
                        for(long i=0;i<nx*ny*nz;i++)\r
index 5ac2eecd1dc3f3c7b1fe8e19c390a0972da71cea..0a90d9597228fd1ad87ad59e4a1f03f7508bda9e 100644 (file)
@@ -517,7 +517,7 @@ void MGL_EXPORT mgl_write_tex(HMGL gr, const char *fname,const char *descr)
        if(!fp)         {       gr->SetWarn(mglWarnOpen,fname); return; }
        setlocale(LC_NUMERIC, "C");
        fprintf(fp, "%% Created by MathGL library\n%% Title: %s\n\n",descr?descr:fname);
-       mreal ms=0.4*gr->mark_size()/100;       // provide marks
+       // provide marks
        fprintf(fp, "\\providecommand{\\mglp}[4]{\\draw[#3] (#1-#4, #2) -- (#1+#4,#2) (#1,#2-#4) -- (#1,#2+#4);}\n");
        fprintf(fp, "\\providecommand{\\mglx}[4]{\\draw[#3] (#1-#4, #2-#4) -- (#1+#4,#2+#4) (#1+#4,#2-#4) -- (#1-#4,#2+#4);}\n");
        fprintf(fp, "\\providecommand{\\mgls}[4]{\\draw[#3] (#1-#4, #2-#4) -- (#1+#4,#2-#4) -- (#1+#4,#2+#4) -- (#1-#4,#2+#4) -- cycle;}\n");
@@ -537,7 +537,7 @@ void MGL_EXPORT mgl_write_tex(HMGL gr, const char *fname,const char *descr)
        fprintf(fp, "\\providecommand{\\mglY}[4]{\\draw[#3] (#1, #2-#4) -- (#1,#2) (#1-#4,#2+#4) -- (#1,#2) (#1+#4,#2+#4) -- (#1,#2);}\n");
        fprintf(fp, "\\providecommand{\\mglo}[4]{\\draw[#3] (#1, #2) circle (#4);}\n");
        fprintf(fp, "\\providecommand{\\mglO}[4]{\\fill[#3] (#1, #2) circle (#4);}\n");
-       fprintf(fp, "\\providecommand{\\mglc}[3]{\\draw[#3] (#1, #2) circle (%g);}\n\n", 0.1*ms);
+       fprintf(fp, "\\providecommand{\\mglc}[3]{\\draw[#3] (#1, #2) circle (%g);}\n\n", 4e-4*gr->mark_size());
        fprintf(fp, "\\begin{tikzpicture}\n");
 
        // write primitives first
index 464456215f818b2da454bef2c78fa607d2ac7757..5b46c572402cb2818c77f0410492976c61de44b6 100644 (file)
@@ -739,7 +739,7 @@ void MGL_EXPORT mgl_flowp_xy(HMGL gr, double x0, double y0, double z0, HCDT x, H
        bool vv = mglchr(sch,'v');\r
        // find coordinates u, v\r
        register long i,j;\r
-       register mreal d, dm=1e7;\r
+       register mreal d, dm=INFINITY;\r
        long i0=0,j0=0;\r
        for(i=0;i<n;i++)        for(j=0;j<m;j++)        // first find closest\r
        {\r
@@ -952,7 +952,7 @@ void MGL_EXPORT mgl_flowp_xyz(HMGL gr, double x0, double y0, double z0, HCDT x,
 \r
        // find coordinates u, v, w\r
        register long i,j,k;\r
-       register mreal d, dm=1e7;\r
+       register mreal d, dm=INFINITY;\r
        long i0=0,j0=0,k0=0;\r
        mreal dx,dy,dz;\r
        for(i=0;i<n;i++)        for(j=0;j<m;j++)        for(k=0;k<l;k++)        // first find closest\r
index 87d8d29933d6ba89ebe0396eb0d7b6c4cee0af98..8bdf906453448708c03a5684a8ac26d081cf60ca 100644 (file)
@@ -2020,11 +2020,16 @@ These functions draw continuous lines between points and fills it to axis plane.
 @anchor{region}
 @deftypefn {MGL command} {} region ydat1 ydat2 ['stl'='']
 @deftypefnx {MGL command} {} region xdat ydat1 ydat2 ['stl'='']
+@deftypefnx {MGL command} {} region xdat1 ydat1 xdat2 ydat2 ['stl'='']
+@deftypefnx {MGL command} {} region xdat1 ydat1 zdat1 xdat2 ydat2 zdat2 ['stl'='']
 @ifclear UDAV
 @deftypefnx {Method on @code{mglGraph}} @code{void} Region (@code{const mglDataA &}y1, @code{const mglDataA &}y2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
 @deftypefnx {Method on @code{mglGraph}} @code{void} Region (@code{const mglDataA &}x, @code{const mglDataA &}y1, @code{const mglDataA &}y2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
+@deftypefnx {Method on @code{mglGraph}} @code{void} Region (@code{const mglDataA &}x1, @code{const mglDataA &}y1, @code{const mglDataA &}x2, @code{const mglDataA &}y2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
+@deftypefnx {Method on @code{mglGraph}} @code{void} Region (@code{const mglDataA &}x1, @code{const mglDataA &}y1, @code{const mglDataA &}z1, @code{const mglDataA &}x2, @code{const mglDataA &}y2, @code{const mglDataA &}z2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
 @deftypefnx {C function} @code{void} mgl_region (@code{HMGL} gr, @code{HCDT} y1, @code{HCDT} y2, @code{const char *}pen, @code{const char *}opt)
 @deftypefnx {C function} @code{void} mgl_region_xy (@code{HMGL} gr, @code{HCDT} x, @code{HCDT} y1, @code{HCDT} y2, @code{const char *}pen, @code{const char *}opt)
+@deftypefnx {C function} @code{void} mgl_region_3d (@code{HMGL} gr, @code{HCDT} x1, @code{HCDT} y1, @code{HCDT} z1, @code{HCDT} x2, @code{HCDT} y2, @code{HCDT} z2, @code{const char *}pen, @code{const char *}opt)
 @end ifclear
 These functions fill area between 2 curves. Dimensions of arrays @var{y1} and @var{y2} must be equal. Also you can use gradient filling if number of specified colors is equal to 2*number of curves. If @var{pen} contain symbol @samp{i} then only area with y1<y<y2 will be filled else the area with y2<y<y1 will be filled too. See also @ref{area}, @ref{bars}, @ref{stem}. @sref{Region sample}
 @end deftypefn
index 8416025976158ea1527d474101b382acaf5a9f5b..5cff8a0a6098f7412f153d6d108943358639b8be 100644 (file)
@@ -1907,11 +1907,16 @@ gr.GetBGRN(bits, len(bits));
 @anchor{region}
 @deftypefn {Команда MGL} {} region ydat1 ydat2 ['stl'='']
 @deftypefnx {Команда MGL} {} region xdat ydat1 ydat2 ['stl'='']
+@deftypefnx {Команда MGL} {} region xdat1 ydat1 xdat2 ydat2 ['stl'='']
+@deftypefnx {Команда MGL} {} region xdat1 ydat1 zdat1 xdat2 ydat2 zdat2 ['stl'='']
 @ifclear UDAV
 @deftypefnx {Метод класса @code{mglGraph}} @code{void} Region (@code{const mglDataA &}y1, @code{const mglDataA &}y2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
 @deftypefnx {Метод класса @code{mglGraph}} @code{void} Region (@code{const mglDataA &}x, @code{const mglDataA &}y1, @code{const mglDataA &}y2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
+@deftypefnx {Метод класса @code{mglGraph}} @code{void} Region (@code{const mglDataA &}x1, @code{const mglDataA &}y1, @code{const mglDataA &}x2, @code{const mglDataA &}y2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
+@deftypefnx {Метод класса @code{mglGraph}} @code{void} Region (@code{const mglDataA &}x1, @code{const mglDataA &}y1, @code{const mglDataA &}z1, @code{const mglDataA &}x2, @code{const mglDataA &}y2, @code{const mglDataA &}z2, @code{const char *}pen=@code{""}, @code{const char *}opt=@code{""})
 @deftypefnx {Функция С} @code{void} mgl_region (@code{HMGL} gr, @code{HCDT} y1, @code{HCDT} y2, @code{const char *}pen, @code{const char *}opt)
 @deftypefnx {Функция С} @code{void} mgl_region_xy (@code{HMGL} gr, @code{HCDT} x, @code{HCDT} y1, @code{HCDT} y2, @code{const char *}pen, @code{const char *}opt)
+@deftypefnx {Функция С} @code{void} mgl_region_3d (@code{HMGL} gr, @code{HCDT} x1, @code{HCDT} y1, @code{HCDT} z1, @code{HCDT} x2, @code{HCDT} y2, @code{HCDT} z2, @code{const char *}pen, @code{const char *}opt)
 @end ifclear
 Функции закрашивают область между 2 кривыми. Градиентная заливка используется если число цветов равно удвоенному число кривых. Если @var{pen} содержит @samp{i}, то закрашивается только область y1<y<y2, в противном случае будет закрашена и область y2<y<y1. См. также @ref{area}, @ref{bars}, @ref{stem}. @sref{Region sample}
 @end deftypefn
index 119a58b428cf782bc6ae8902393b694238437e13..a5dca99bd6439ec1f46486dd46cd7f9080ea56f0 100644 (file)
@@ -257,7 +257,41 @@ int sample(mglGraph *gr)
 @end verbatim
 First, the function creates a frame by calling @code{NewFrame()} for rotated axes and draws the bounding box.  The function @code{EndFrame()} @strong{must be} called after the frame drawing! The second frame contains the bounding box and axes @code{Axis("xy")} in the initial (unrotated) coordinates. Function @code{sample} returns the number of created frames @code{GetNumFrame()}.
 
-Note, that such kind of animation is rather slow and not well suitable for visualization of running calculations. For the last case one can use @code{Update()} function. The most simple case for doing this is to use @code{mglDraw} class and reimplement its @code{Calc()} method.
+Note, that such kind of animation is rather slow and not well suitable for visualization of running calculations. For the last case one can use @code{Update()} function. The most simple case for doing this is running yours calculations firstly in separate thread and later start MathGL window (QT or FLTK) creation.
+@verbatim
+#include <mgl2/window.h>
+mglWindow *gr=NULL;
+void *calc(void *)
+{
+  mglPoint pnt;
+  for(int i=0;i<10;i++)   // do calculation
+  {
+    sleep(2);             // which can be very long
+    pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);
+    if(gr)            // be sure that window is ready
+    {
+      gr->Clf();      // make new drawing
+      gr->Line(mglPoint(),pnt,"Ar2");
+      char str[10] = "i=0";  str[2] = '0'+i;
+      gr->Puts(mglPoint(),str);
+      gr->Update();   // update window
+    }
+  }
+  exit(0);
+}
+int main(int argc,char **argv)
+{
+  static pthread_t thr;
+  // first run yours routine in separate thread
+  pthread_create(&thr,0,calc,0);
+  pthread_detach(thr);
+  gr = new mglWindow;
+  gr->Run();  return 0;
+}
+@end verbatim
+Note, that such method looks as not working for GLUT windows due to limitation of @code{glutMainLoop()} function.
+
+Another ways use built-in MathGL feature to run a member function @code{mglDraw::Calc()} separate thread, which work only if pthread support is enabled. For this, you just need to use @code{mglDraw} class and reimplement its @code{Calc()} method.
 @verbatim
 #include <mgl2/window.h>
 class Foo : public mglDraw
@@ -294,11 +328,12 @@ int main(int argc,char **argv)
 }
 @end verbatim
 
-Previous sample can be run in C++ only since it use C++ class mglDraw. However similar idea can be used even in Fortran or SWIG-based (Python/Octave/...) if one use FLTK window. Such limitation come from the Qt requirement to be run in the primary thread only. The sample code will be:
+Finally, you can put the event-handling loop in separate instead of yours code by using @code{RunThr()} function instead of @code{Run()} one. Unfortunately, such method work well only for FLTK windows and only if pthread support was enabled. Such limitation come from the Qt requirement to be run in the primary thread only. The sample code will be:
 @verbatim
+#include <mgl2/fltk.h>
 int main(int argc,char **argv)
 {
-  mglWindow gr("test");   // create window
+  mglFLTK gr("test");     // create window
   gr.RunThr();            // run event loop in separate thread
   for(int i=0;i<10;i++)   // do calculation
   {
@@ -307,14 +342,13 @@ int main(int argc,char **argv)
     gr.Clf();             // make new drawing
     gr.Line(mglPoint(),pnt,"Ar2");
     char str[10] = "i=0"; str[2] = '0'+i;
-    gr.Puts(mglPoint(),"");
+    gr.Puts(mglPoint(),str);
     gr.Update();          // update window when you need it
   }
   return 0;   // finish calculations and close the window
 }
 @end verbatim
 
-
 Pictures with @strong{animation can be saved in file(s)} as well. You can: export in animated GIF, or save each frame in separate file (usually JPEG) and convert these files into the movie (for example, by help of ImageMagic). Let me show both methods.
 
 @anchor{GIF}
@@ -4055,6 +4089,10 @@ In version 2.0, main classes (@code{mglGraph} and @code{mglData}) contains only
 
 Note, that you have to make import library(-ies) *.lib for provided binary *.dll. This procedure depend on used compiler -- please read documentation for yours compiler. For VisualStudio, it can be done by command @code{lib.exe /DEF:libmgl.def /OUT:libmgl.lib}.
 
+@item How make FLTK/GLUT/Qt window which will display result of my calculations?
+
+You need to put yours calculations or main event-handling loop in the separate thread. For static image you can give @code{NULL} as drawing function and call @code{Update()} function when you need to redraw it. For more details see @ref{Animation}.
+
 @item How I can build MathGL under Windows?
 Generally, it is the same procedure as for Linux or MacOS -- see section @ref{Installation}. The simplest way is using the combination CMake+MinGW. Also you may need some extra libraries like GSL, PNG, JPEG and so on. All of them can be found at @url{http://gnuwin32.sourceforge.net/packages.html}. After installing all components, just run @uref{http://www.cmake.org/cmake/help/runningcmake.html, cmake-gui} configurator and build the MathGL itself.
 
index 6b765c77e13d2623e5a8317e7dbe4d37dc60e2cc..f6001f171c94139dbb5450054ba731c889057f76 100644 (file)
@@ -256,7 +256,41 @@ int sample(mglGraph *gr)
 @end verbatim
 First, the function creates a frame by calling @code{NewFrame()} for rotated axes and draws the bounding box.  The function @code{EndFrame()} @strong{must be} called after the frame drawing! The second frame contains the bounding box and axes @code{Axis("xy")} in the initial (unrotated) coordinates. Function @code{sample} returns the number of created frames @code{GetNumFrame()}.
 
-Note, that such kind of animation is rather slow and not well suitable for visualization of running calculations. For the last case one can use @code{Update()} function. The most simple case for doing this is to use @code{mglDraw} class and reimplement its @code{Calc()} method.
+Note, that such kind of animation is rather slow and not well suitable for visualization of running calculations. For the last case one can use @code{Update()} function. The most simple case for doing this is running yours calculations firstly in separate thread and later start MathGL window (QT or FLTK) creation.
+@verbatim
+#include <mgl2/window.h>
+mglWindow *gr=NULL;
+void *calc(void *)
+{
+  mglPoint pnt;
+  for(int i=0;i<10;i++)   // do calculation
+  {
+    sleep(2);             // which can be very long
+    pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);
+    if(gr)            // be sure that window is ready
+    {
+      gr->Clf();      // make new drawing
+      gr->Line(mglPoint(),pnt,"Ar2");
+      char str[10] = "i=0";  str[2] = '0'+i;
+      gr->Puts(mglPoint(),str);
+      gr->Update();   // update window
+    }
+  }
+  exit(0);
+}
+int main(int argc,char **argv)
+{
+  static pthread_t thr;
+  // first run yours routine in separate thread
+  pthread_create(&thr,0,calc,0);
+  pthread_detach(thr);
+  gr = new mglWindow;
+  gr->Run();  return 0;
+}
+@end verbatim
+Note, that such method looks as not working for GLUT windows due to limitation of @code{glutMainLoop()} function.
+
+Another ways use built-in MathGL feature to run a member function @code{mglDraw::Calc()} separate thread, which work only if pthread support is enabled. For this, you just need to use @code{mglDraw} class and reimplement its @code{Calc()} method.
 @verbatim
 #include <mgl2/window.h>
 class Foo : public mglDraw
@@ -293,11 +327,12 @@ int main(int argc,char **argv)
 }
 @end verbatim
 
-Previous sample can be run in C++ only since it use C++ class mglDraw. However similar idea can be used even in Fortran or SWIG-based (Python/Octave/...) if one use FLTK window. Such limitation come from the Qt requirement to be run in the primary thread only. The sample code will be:
+Finally, you can put the event-handling loop in separate instead of yours code by using @code{RunThr()} function instead of @code{Run()} one. Unfortunately, such method work well only for FLTK windows and only if pthread support was enabled. Such limitation come from the Qt requirement to be run in the primary thread only. The sample code will be:
 @verbatim
+#include <mgl2/fltk.h>
 int main(int argc,char **argv)
 {
-  mglWindow gr("test");   // create window
+  mglFLTK gr("test");     // create window
   gr.RunThr();            // run event loop in separate thread
   for(int i=0;i<10;i++)   // do calculation
   {
@@ -306,7 +341,7 @@ int main(int argc,char **argv)
     gr.Clf();             // make new drawing
     gr.Line(mglPoint(),pnt,"Ar2");
     char str[10] = "i=0"; str[2] = '0'+i;
-    gr.Puts(mglPoint(),"");
+    gr.Puts(mglPoint(),str);
     gr.Update();          // update window when you need it
   }
   return 0;   // finish calculations and close the window
@@ -4067,63 +4102,7 @@ gr->Window(argc,argv,foo_draw,"Title",this);
 Простейший путь -- использование комбинации CMake и MinGW. Также Вам может потребоваться дополнительные библиотеки, такие как GSL, PNG, JPEG и пр. Все они могут быть найдены на @url{http://gnuwin32.sourceforge.net/packages.html}. После установки всех компонент, просто запустите конфигуратор CMake и соберите MathGL командой make.
 
 @item Как создать окно FLTK/GLUT/Qt с текущими результатами параллельно с выполнением основных вычислений?
-Следует создать отдельный поток для обработки сообщений в окно. Кросс-платформенный путь -- использование библиотеки @code{pthread}. Обновление данных в окне можно выполнить вызовом функции @code{mglGraphFLTK::Update()}. Пример код имеет вид:
-@verbatim
-//-----------------------------------------------------------------------------
-#include <mgl2/window.h>
-#include <pthread.h>
-
-mglPoint pnt;  // some global variable for changable data
-//-----------------------------------------------------------------------------
-int sample(mglGraph *gr)
-{
-  gr->Box();  gr->Line(mglPoint(),pnt,"Ar2"); // just draw a vector
-  return 0;
-}
-//-----------------------------------------------------------------------------
-void *mgl_fltk_tmp(void *)      {       mglFlRun();     return 0;       }
-int main (int argc, char ** argv)
-{
-  mglWindow gr(0,sample,"test");  // create window
-  static pthread_t tmp;
-  pthread_create(&tmp, 0, mgl_fltk_tmp, 0);
-  pthread_detach(tmp);    // run window handling in the separate thread
-  for(int i=0;i<10;i++)   // do calculation
-  {
-    sleep(1);             // which can be very long
-    pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);
-    gr.Update();          // update window
-  }
-  return 0;   // finish calculations and close the window
-}
-//-----------------------------------------------------------------------------
-@end verbatim
-В случае если требуется вывести статичную картинку с текущими результатами расчетов, то достаточно передать @code{NULL} вместо функции рисования и вызывать @code{Update()} по мере необходимости для обновления графика. Такой способ подходит и для пользователей фортрана.
-@verbatim
-//-----------------------------------------------------------------------------
-#include <mgl2/window.h>
-//-----------------------------------------------------------------------------
-int sample(mglGraph *gr)
-{
-  gr->Box();  gr->Line(mglPoint(),pnt,"Ar2"); // just draw a vector
-  return 0;
-}
-//-----------------------------------------------------------------------------
-void *mgl_fltk_tmp(void *)      {       mglFlRun();     return 0;       }
-int main (int argc, char ** argv)
-{
-  mglWindow gr(0,NULL,"test");  // create window
-  for(int i=0;i<10;i++)   // do calculation
-  {
-    sleep(1);             // which can be very long
-    pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1);
-    sample(&gr);          // draw picture
-    gr.Update();          // update window
-  }
-  return 0;   // finish calculations and close the window
-}
-//-----------------------------------------------------------------------------
-@end verbatim
+Следует создать отдельный поток для обработки сообщений в окно. Обновление данных в окне можно выполнить вызовом функции @code{Update()}. Подробнее см. @ref{Animation}.
 
 @item Сколько человек участвовало в создании библиотеки?
 Большую часть библиотеки написал один человек. Это результат примерно года работы на написание ядра библиотеки и базовых функций (в основном вечерами и по выходным). Процесс усовершенствования продолжается и теперь :). Скрипты сборки в основном написаны Д.Кулагиным, а экспорт в PRC/PDF написан М.Видассовым.
index a53255eed95a4ad7eff368a50e692671bd5db480..3b5769397f26a0f810dc504c744d1822bd371768 100644 (file)
@@ -1,3 +1,3 @@
 @set VERSION 2.2
-@set MINVER .1
+@set MINVER .2
 @c @set MINVER 
index fa59be61c84a3c604d53cb9ff18ad7f6d66aad1f..29c5289c1050d65d41600f2db7a376c928ea0cab 100644 (file)
@@ -1,3 +1,8 @@
+2.2.2 Released 10 March 2014
+2.2.1 Released 22 January 2014
+2.2 Released 11 November 2013
+2.1.3.1 Released 8 May 2013
+2.1.3 Released 2 May 2013
 2.1.2  Released 28 January 2013
 2.1.1  Released 24 December 2012
 2.1    Released 13 December 2012
index 51f5b64927dba7aa7aad332ac3d4660211ca9aa8..4a39ffaeae0854ead1d18e130f831e28c1494765 100644 (file)
@@ -52,6 +52,8 @@ Generally, MathGL is GPL library. However, you can use LGPL license for MathGL c
 
 @strong{Latest news}
 @itemize
+@item @emph{10 March 2014.}
+New version (v.@value{VERSION}@value{MINVER}) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are extend of 'region' plot, improve export to TeX, add missing Fortran functions, bugfixes, and other improvements, which denoted @ref{News, here}. Note, this release looks as bug free, but next release (v.2.3) will introduce a set of improvements which may not so stable at first time.
 @item @emph{22 January 2014.}
 New version (v.@value{VERSION}) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are support of Qt5 and Pascal, improvements in JavaScript interface, bugfixes, and other improvements, which denoted @ref{News, here}.
 @item @emph{11 November 2013.}
@@ -74,6 +76,18 @@ Javascript interface was developed with support of @url{http://www.datadvance.ne
 @nav{}
 
 @itemize
+@item
+@strong{10 March 2014.}
+New version (v.2.2.2) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are minor improvements and bugfixes:
+@itemize @bullet
+@item Add mgl_region_3d() to draw region (or ribbon) between 2 curves. Correspondingly extend mglGraph::Region() function and MGL command 'region'.
+@item Allow LGPL for MathGL widgets.
+@item Improve export to TeX.
+@item Add missing functions to Fortran interface.
+@item Bugfix for legend with enabled lighting.
+@item Minor bugfixes and memory leaks.
+@end itemize
+
 @item
 @strong{22 January 2014.}
 New version (v.2.2.1) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are minor improvements and bugfixes:
index 51f5b64927dba7aa7aad332ac3d4660211ca9aa8..4a39ffaeae0854ead1d18e130f831e28c1494765 100644 (file)
@@ -52,6 +52,8 @@ Generally, MathGL is GPL library. However, you can use LGPL license for MathGL c
 
 @strong{Latest news}
 @itemize
+@item @emph{10 March 2014.}
+New version (v.@value{VERSION}@value{MINVER}) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are extend of 'region' plot, improve export to TeX, add missing Fortran functions, bugfixes, and other improvements, which denoted @ref{News, here}. Note, this release looks as bug free, but next release (v.2.3) will introduce a set of improvements which may not so stable at first time.
 @item @emph{22 January 2014.}
 New version (v.@value{VERSION}) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are support of Qt5 and Pascal, improvements in JavaScript interface, bugfixes, and other improvements, which denoted @ref{News, here}.
 @item @emph{11 November 2013.}
@@ -74,6 +76,18 @@ Javascript interface was developed with support of @url{http://www.datadvance.ne
 @nav{}
 
 @itemize
+@item
+@strong{10 March 2014.}
+New version (v.2.2.2) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are minor improvements and bugfixes:
+@itemize @bullet
+@item Add mgl_region_3d() to draw region (or ribbon) between 2 curves. Correspondingly extend mglGraph::Region() function and MGL command 'region'.
+@item Allow LGPL for MathGL widgets.
+@item Improve export to TeX.
+@item Add missing functions to Fortran interface.
+@item Bugfix for legend with enabled lighting.
+@item Minor bugfixes and memory leaks.
+@end itemize
+
 @item
 @strong{22 January 2014.}
 New version (v.2.2.1) of @uref{http://sourceforge.net/projects/mathgl, MathGL} is released. There are minor improvements and bugfixes:
index d3da54c2e357a99a9a95c6ecca8b6c51cd5df66c..398fa4b11d9f74404681e9128963ee3afcc9f277 100644 (file)
--- a/todo.txt
+++ b/todo.txt
 11. Check centered curved text (see text2)
 12. Export to X3D
 
-16. Inplot data should have ranges (add mglInPlot{x1,x2,y1,y2,Bp or something like this} which include mglMatrix instead of mglBase::Bp) + calc coor + JS
-17. 'perspective' command in UDAV
-18. Check octave 3.8
-21. Use mglFormulaCalc() for data transformation/filling (much faster but require more memory) + the same for complex
-22. Test mglDataC::Diffraction() + write sample + add rational function???
+13. Inplot data should have ranges (add mglInPlot{x1,x2,y1,y2,Bp or something like this} which include mglMatrix instead of mglBase::Bp) + calc coor + JS
+14. 'perspective' command in UDAV
+15. Check octave 3.8
+16. Use mglFormulaCalc() for data transformation/filling (much faster but require more memory) + the same for complex
+17. Test mglDataC::Diffraction() + write sample + add rational function???
 
-24. Use mglStack<T> instead of std::vector<T> due to multi-threading
-25. Get true coordinates in CalcXYZ for curved equations too
-27. Add mglDataVar, mglDataCol, mglDataFunc for handling special (temporary) data + add real(), imag(), conj() + accurate types in MGL + add 'expr'/'complex' in MGL
+18. Use mglStack<T> instead of std::vector<T> due to multi-threading
+19. Get true coordinates in CalcXYZ for curved equations too
+20. Add mglDataVar, mglDataCol, mglDataFunc for handling special (temporary) data + add real(), imag(), conj() + accurate types in MGL + add 'expr'/'complex' in MGL
+
+21. Check if Fortran versions possible for: mgl_data_info, mgl_datas_hdf, mgl_get_fit, mgl_get_mess, mgl_get_plotid
 
-28. Check if Fortran versions possible for: mgl_data_info, mgl_datas_hdf, mgl_get_fit, mgl_get_mess, mgl_get_plotid
 
 ============= DOCUMENTATION =============
 
index 2e1807566e300eccc31f305a915376860601a75c..2ecd8c00259b45adc709ee5a0e20da121f17dda6 100644 (file)
@@ -211,7 +211,7 @@ void mglCanvasGLUT::Window(int argc, char **argv,int (*draw)(mglBase *gr, void *
        delete []tmp[0];\r
        glutInitDisplayMode(GLUT_RGB);\r
        glutInitWindowSize(600, 600);\r
-       glutCreateWindow("MathPlotLibrary");\r
+       glutCreateWindow("MathGL");\r
 \r
        AddLight(0,mglPoint(0,0,3),false);\r
        if(draw)\r
@@ -231,7 +231,7 @@ void mglCanvasGLUT::Window(int argc, char **argv,int (*draw)(mglBase *gr, void *
        // TODO Add window maximazing at start up ???\r
 \r
        glutMainLoop();\r
-       glDeleteLists(1,NumFig);\r
+       if(NumFig>0)    glDeleteLists(1,NumFig);\r
 }\r
 //-----------------------------------------------------------------------------\r
 HMGL MGL_EXPORT mgl_create_graph_glut(int (*draw)(HMGL gr, void *p), const char *title, void *par, void (*load)(void *p))\r