From: Dimitrios Eftaxiopoulos Date: Sat, 15 Mar 2014 09:41:40 +0000 (+0200) Subject: Imported Upstream version 2.2.2+svn930 X-Git-Tag: archive/raspbian/2.5-2+rpi1^2~26^2~14 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=ab9a3732428a0f680fda1f0174569c6cf51541a0;p=mathgl.git Imported Upstream version 2.2.2+svn930 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 2611170..f279dce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/ChangeLog.txt b/ChangeLog.txt index efab471..0cd0084 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -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 diff --git a/examples/fltk_example.cpp b/examples/fltk_example.cpp index 993fb5a..52cce1d 100644 --- a/examples/fltk_example.cpp +++ b/examples/fltk_example.cpp @@ -18,44 +18,76 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "mgl2/fltk.h" +//----------------------------------------------------------------------------- #if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) #include #else #include #endif +void long_calculations() // just delay which correspond to simulate calculations +{ +#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) + Sleep(1000); +#else + sleep(1); // which can be very long +#endif +} //----------------------------------------------------------------------------- -int test_wnd(mglGraph *gr); -int sample(mglGraph *gr); -int sample_1(mglGraph *gr); -int sample_2(mglGraph *gr); -int sample_3(mglGraph *gr); -int sample_d(mglGraph *gr); -//----------------------------------------------------------------------------- -mglPoint pnt; // some global variable for changeable data -void *mgl_fltk_tmp(void *) { mgl_fltk_run(); return 0; } -//#define PTHREAD_SAMPLE -//----------------------------------------------------------------------------- +#if defined(PTHREAD_SAMPLE1) // first variant of multi-threading usage of mglFLTK window +mglFLTK *gr=NULL; +void *calc(void *) +{ + mglPoint pnt; + for(int i=0;i<10;i++) // do calculation + { + long_calculations(); // which can be very long + pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1); + if(gr) + { + 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; + pthread_create(&thr,0,calc,0); + pthread_detach(thr); + gr = new mglFLTK; + gr->Run(); return 0; +} +#elif defined(PTHREAD_SAMPLE2) // another variant of multi-threading usage of mglFLTK window. Work only if pthread was enabled for MathGL +mglPoint pnt; // some global variable for changeable data int main(int argc,char **argv) { -#ifdef PTHREAD_SAMPLE mglFLTK gr("test"); - gr.RunThr(); + gr.RunThr(); // <-- need MathGL version which use pthread for(int i=0;i<10;i++) // do calculation { -#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) - Sleep(1000); -#else - sleep(1); // which can be very long -#endif + long_calculations();// which can be very long pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1); gr.Clf(); // make new drawing gr.Line(mglPoint(),pnt,"Ar2"); char str[10] = "i=0"; str[3] = '0'+i; - gr.Puts(mglPoint(),""); gr.Update(); // update window } return 0; // finish calculations and close the window -#else +} +#else // just default samples +int test_wnd(mglGraph *gr); +int sample(mglGraph *gr); +int sample_1(mglGraph *gr); +int sample_2(mglGraph *gr); +int sample_3(mglGraph *gr); +int sample_d(mglGraph *gr); +//----------------------------------------------------------------------------- +int main(int argc,char **argv) +{ mglFLTK *gr; char key = 0; if(argc>1) key = argv[1][0]!='-' ? argv[1][0]:argv[1][1]; @@ -70,6 +102,5 @@ int main(int argc,char **argv) default: gr = new mglFLTK(sample,"Drop and waves"); break; } gr->Run(); return 0; -#endif } -//----------------------------------------------------------------------------- +#endif diff --git a/examples/full_test.cpp b/examples/full_test.cpp index 2032ad7..3d34d84 100644 --- a/examples/full_test.cpp +++ b/examples/full_test.cpp @@ -69,6 +69,16 @@ void smgl_surf(mglGraph *gr); #include void test(mglGraph *gr) { + mglData y(50); + y.Modify("sin(10*x) + 10"); + gr->SetRange('y', y); + gr->Box(); + gr->Axis(); + gr->Plot(y); + y.Save("test.dat"); + return; + + mglParse par; setlocale(LC_CTYPE, ""); par.Execute(gr,"new x 50 40 '0.8*sin(pi*x)*sin(pi*(y+1)/2)'\n\ diff --git a/examples/glut_example.cpp b/examples/glut_example.cpp index 35d1a08..4018362 100644 --- a/examples/glut_example.cpp +++ b/examples/glut_example.cpp @@ -19,6 +19,51 @@ ***************************************************************************/ #include "mgl2/glut.h" //----------------------------------------------------------------------------- +#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) +#include +#else +#include +#endif +void long_calculations() // just delay which correspond to simulate calculations +{ +#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) + Sleep(1000); +#else + sleep(1); // which can be very long +#endif +} +//----------------------------------------------------------------------------- +#if defined(PTHREAD_SAMPLE) +mglGLUT *gr=NULL; +void *calc(void *) +{ + mglPoint pnt; + 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); +printf("i=%d, gr=%p\n",i,gr); fflush(stdout); + if(gr) + { + 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; + pthread_create(&thr,0,calc,0); + pthread_detach(thr); + gr = new mglGLUT; +printf("Create gr=%p\n",gr); fflush(stdout); + gr->Run(); return 0; +} +#else int test_wnd(mglGraph *gr); int sample(mglGraph *gr); int sample_m(mglGraph *gr); @@ -33,19 +78,16 @@ int main(int argc,char **argv) char key = 0; if(argc>1) key = argv[1][0]!='-' ? argv[1][0] : argv[1][1]; else printf("You may specify argument '1', '2', '3' or 'd' for viewing examples of 1d, 2d, 3d or dual plotting\n"); - - const char *desc; - draw_func func; + mglGLUT *gr; switch(key) { - case '1': func = sample_1; desc = "1D plots"; break; - case '2': func = sample_2; desc = "2D plots"; break; - case '3': func = sample_3; desc = "3D plots"; break; - case 'd': func = sample_d; desc = "Dual plots"; break; - case 't': func = test_wnd; desc = "Testing"; break; - default: func = sample; desc = "Example of molecules"; break; + case '1': gr = new mglGLUT(sample_1, "1D plots"); break; + case '2': gr = new mglGLUT(sample_2, "2D plots"); break; + case '3': gr = new mglGLUT(sample_3, "3D plots"); break; + case 'd': gr = new mglGLUT(sample_d, "Dual plots"); break; + case 't': gr = new mglGLUT(test_wnd, "Testing"); break; + default: gr = new mglGLUT(sample, "Example of molecules"); break; } - mglGLUT gr(func,desc); return 0; } -//----------------------------------------------------------------------------- +#endif \ No newline at end of file diff --git a/examples/qt_example.cpp b/examples/qt_example.cpp index 5a10eb9..54cfbcf 100644 --- a/examples/qt_example.cpp +++ b/examples/qt_example.cpp @@ -18,23 +18,50 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "mgl2/qt.h" +//----------------------------------------------------------------------------- #if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) #include #else #include #endif +void long_calculations() // just delay which correspond to simulate calculations +{ +#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) + Sleep(1000); +#else + sleep(1); // which can be very long +#endif +} //----------------------------------------------------------------------------- -int test_wnd(mglGraph *gr); -int sample(mglGraph *gr); -int sample_1(mglGraph *gr); -int sample_2(mglGraph *gr); -int sample_3(mglGraph *gr); -int sample_d(mglGraph *gr); -//----------------------------------------------------------------------------- -//#define PTHREAD_SAMPLE -mglPoint pnt; // some global variable for changeable data -void *mgl_qt_tmp(void *); -//----------------------------------------------------------------------------- +#if defined(PTHREAD_SAMPLE1) // first variant of multi-threading usage of mglQT window +mglQT *gr=NULL; +void *calc(void *) +{ + mglPoint pnt; + for(int i=0;i<10;i++) // do calculation + { + long_calculations(); // which can be very long + pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1); + if(gr) + { + 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; + pthread_create(&thr,0,calc,0); + pthread_detach(thr); + gr = new mglQT; + gr->Run(); return 0; +} +#elif defined(PTHREAD_SAMPLE2) // another variant of multi-threading usage of mglQT window. Work only if pthread was enabled for MathGL class Foo : public mglDraw { mglPoint pnt; // some result of calculation @@ -43,36 +70,39 @@ public: int Draw(mglGraph *gr); void Calc(); }; -//----------------------------------------------------- void Foo::Calc() { - for(int i=0;i<30;i++) // do calculation + for(int i=0;i<30;i++) // do calculation { -#if defined(WIN32) || defined(_MSC_VER) || defined(__BORLANDC__) - Sleep(1000); -#else - sleep(1); // which can be very long -#endif + long_calculations(); // which can be very long pnt = mglPoint(2*mgl_rnd()-1,2*mgl_rnd()-1); - Gr->Update(); // update window + Gr->Update(); // update window } } -//----------------------------------------------------- int Foo::Draw(mglGraph *gr) { gr->Line(mglPoint(),pnt,"Ar2"); gr->Box(); return 0; } -//----------------------------------------------------- int main(int argc,char **argv) { -#ifdef PTHREAD_SAMPLE Foo *foo = new Foo; mglQT gr(foo,"MathGL examples"); - foo->Gr = &gr; foo->Run(); + foo->Gr = &gr; + foo->Run(); // <-- need MathGL version which use pthread return gr.Run(); -#else +} +#else // just default samples +int test_wnd(mglGraph *gr); +int sample(mglGraph *gr); +int sample_1(mglGraph *gr); +int sample_2(mglGraph *gr); +int sample_3(mglGraph *gr); +int sample_d(mglGraph *gr); +//----------------------------------------------------------------------------- +int main(int argc,char **argv) +{ mglQT *gr; char key = 0; if(argc>1) key = argv[1][0]!='-' ? argv[1][0]:argv[1][1]; @@ -87,6 +117,5 @@ int main(int argc,char **argv) default: gr = new mglQT(sample,"Drop and waves"); break; } gr->Run(); return 0; -#endif } -//----------------------------------------------------------------------------- +#endif diff --git a/examples/samples.cpp b/examples/samples.cpp index 5aafe5e..7f30f30 100644 --- a/examples/samples.cpp +++ b/examples/samples.cpp @@ -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:"); diff --git a/include/mgl2/data.h b/include/mgl2/data.h index 44c3934..81aec44 100644 --- a/include/mgl2/data.h +++ b/include/mgl2/data.h @@ -445,8 +445,6 @@ public: #endif #if MGL_NO_DATA_A inline long GetNN() const { return nx*ny*nz; } -#else -protected: #endif /// Get the value in given cell of the data without border checking inline mreal v(long i,long j=0,long k=0) const diff --git a/include/mgl2/datac.h b/include/mgl2/datac.h index c3d94e9..14218d9 100644 --- a/include/mgl2/datac.h +++ b/include/mgl2/datac.h @@ -367,11 +367,7 @@ public: /// Direct access to the data cell inline dual &operator[](long i) { return a[i]; } #endif -#if MGL_NO_DATA_A - inline long GetNN() const { return nx*ny*nz; } -#else -protected: -#endif + /// Get the value in given cell of the data without border checking inline mreal v(long i,long j=0,long k=0) const #ifdef DEBUG diff --git a/include/mgl2/define.h b/include/mgl2/define.h index 3a6d439..c9bd1bb 100644 --- a/include/mgl2/define.h +++ b/include/mgl2/define.h @@ -93,10 +93,13 @@ typedef unsigned long uintptr_t; #if defined(_MSC_VER) || defined(__BORLANDC__) #include +#include const unsigned long mgl_nan[2] = {0xffffffff, 0x7fffffff}; #define NANd (*(double*)mgl_nan) #define NANf (*(float*)&(mgl_nan[1])) + +#if !defined(NAN) #if MGL_USE_DOUBLE #define NAN NANd #else @@ -104,6 +107,8 @@ const unsigned long mgl_nan[2] = {0xffffffff, 0x7fffffff}; #endif #endif +#endif + #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ #endif diff --git a/include/mgl2/glut.h b/include/mgl2/glut.h index 6e360d5..6d6f295 100644 --- a/include/mgl2/glut.h +++ b/include/mgl2/glut.h @@ -75,6 +75,7 @@ public: { mgl_glut_prev_frame(gr); } inline void Animation() ///< Run slideshow (animation) of frames { mgl_glut_animation(gr); } + inline int Run() {}; ///< Run main loop for event handling (placed for similarity to mglWnd) }; //----------------------------------------------------------------------------- #endif diff --git a/json/index.html b/json/index.html index 7802ff0..2bcda48 100644 --- a/json/index.html +++ b/json/index.html @@ -2,7 +2,7 @@ - + diff --git a/json/sylvester.js b/json/sylvester.js deleted file mode 100644 index 3e83bee..0000000 --- a/json/sylvester.js +++ /dev/null @@ -1 +0,0 @@ -eval(function(p,a,c,k,e,r){e=function(c){return(c35?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;j17.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;j3||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 index 0000000..97a6491 --- /dev/null +++ b/json/sylvester.src.js @@ -0,0 +1,1254 @@ +// === Sylvester === +// Vector and Matrix mathematics modules for JavaScript +// Copyright (c) 2007 James Coglan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +var Sylvester = { + version: '0.1.3', + precision: 1e-6 +}; + +function Vector() {} +Vector.prototype = { + + // Returns element i of the vector + e: function(i) { + return (i < 1 || i > this.elements.length) ? null : this.elements[i-1]; + }, + + // Returns the number of elements the vector has + dimensions: function() { + return this.elements.length; + }, + + // Returns the modulus ('length') of the vector + modulus: function() { + return Math.sqrt(this.dot(this)); + }, + + // Returns true iff the vector is equal to the argument + eql: function(vector) { + var n = this.elements.length; + var V = vector.elements || vector; + if (n != V.length) { return false; } + do { + if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; } + } while (--n); + return true; + }, + + // Returns a copy of the vector + dup: function() { + return Vector.create(this.elements); + }, + + // Maps the vector to another vector according to the given function + map: function(fn) { + var elements = []; + this.each(function(x, i) { + elements.push(fn(x, i)); + }); + return Vector.create(elements); + }, + + // Calls the iterator for each element of the vector in turn + each: function(fn) { + var n = this.elements.length, k = n, i; + do { i = k - n; + fn(this.elements[i], i+1); + } while (--n); + }, + + // Returns a new vector created by normalizing the receiver + toUnitVector: function() { + var r = this.modulus(); + if (r === 0) { return this.dup(); } + return this.map(function(x) { return x/r; }); + }, + + // Returns the angle between the vector and the argument (also a vector) + angleFrom: function(vector) { + var V = vector.elements || vector; + var n = this.elements.length, k = n, i; + if (n != V.length) { return null; } + var dot = 0, mod1 = 0, mod2 = 0; + // Work things out in parallel to save time + this.each(function(x, i) { + dot += x * V[i-1]; + mod1 += x * x; + mod2 += V[i-1] * V[i-1]; + }); + mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2); + if (mod1*mod2 === 0) { return null; } + var theta = dot / (mod1*mod2); + if (theta < -1) { theta = -1; } + if (theta > 1) { theta = 1; } + return Math.acos(theta); + }, + + // Returns true iff the vector is parallel to the argument + isParallelTo: function(vector) { + var angle = this.angleFrom(vector); + return (angle === null) ? null : (angle <= Sylvester.precision); + }, + + // Returns true iff the vector is antiparallel to the argument + isAntiparallelTo: function(vector) { + var angle = this.angleFrom(vector); + return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision); + }, + + // Returns true iff the vector is perpendicular to the argument + isPerpendicularTo: function(vector) { + var dot = this.dot(vector); + return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision); + }, + + // Returns the result of adding the argument to the vector + add: function(vector) { + var V = vector.elements || vector; + if (this.elements.length != V.length) { return null; } + return this.map(function(x, i) { return x + V[i-1]; }); + }, + + // Returns the result of subtracting the argument from the vector + subtract: function(vector) { + var V = vector.elements || vector; + if (this.elements.length != V.length) { return null; } + return this.map(function(x, i) { return x - V[i-1]; }); + }, + + // Returns the result of multiplying the elements of the vector by the argument + multiply: function(k) { + return this.map(function(x) { return x*k; }); + }, + + x: function(k) { return this.multiply(k); }, + + // Returns the scalar product of the vector with the argument + // Both vectors must have equal dimensionality + dot: function(vector) { + var V = vector.elements || vector; + var i, product = 0, n = this.elements.length; + if (n != V.length) { return null; } + do { product += this.elements[n-1] * V[n-1]; } while (--n); + return product; + }, + + // Returns the vector product of the vector with the argument + // Both vectors must have dimensionality 3 + cross: function(vector) { + var B = vector.elements || vector; + if (this.elements.length != 3 || B.length != 3) { return null; } + var A = this.elements; + return Vector.create([ + (A[1] * B[2]) - (A[2] * B[1]), + (A[2] * B[0]) - (A[0] * B[2]), + (A[0] * B[1]) - (A[1] * B[0]) + ]); + }, + + // Returns the (absolute) largest element of the vector + max: function() { + var m = 0, n = this.elements.length, k = n, i; + do { i = k - n; + if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; } + } while (--n); + return m; + }, + + // Returns the index of the first match found + indexOf: function(x) { + var index = null, n = this.elements.length, k = n, i; + do { i = k - n; + if (index === null && this.elements[i] == x) { + index = i + 1; + } + } while (--n); + return index; + }, + + // Returns a diagonal matrix with the vector's elements as its diagonal elements + toDiagonalMatrix: function() { + return Matrix.Diagonal(this.elements); + }, + + // Returns the result of rounding the elements of the vector + round: function() { + return this.map(function(x) { return Math.round(x); }); + }, + + // Returns a copy of the vector with elements set to the given value if they + // differ from it by less than Sylvester.precision + snapTo: function(x) { + return this.map(function(y) { + return (Math.abs(y - x) <= Sylvester.precision) ? x : y; + }); + }, + + // Returns the vector's distance from the argument, when considered as a point in space + distanceFrom: function(obj) { + if (obj.anchor) { return obj.distanceFrom(this); } + var V = obj.elements || obj; + if (V.length != this.elements.length) { return null; } + var sum = 0, part; + this.each(function(x, i) { + part = x - V[i-1]; + sum += part * part; + }); + return Math.sqrt(sum); + }, + + // Returns true if the vector is point on the given line + liesOn: function(line) { + return line.contains(this); + }, + + // Return true iff the vector is a point in the given plane + liesIn: function(plane) { + return plane.contains(this); + }, + + // Rotates the vector about the given object. The object should be a + // point if the vector is 2D, and a line if it is 3D. Be careful with line directions! + rotate: function(t, obj) { + var V, R, x, y, z; + switch (this.elements.length) { + case 2: + V = obj.elements || obj; + if (V.length != 2) { return null; } + R = Matrix.Rotation(t).elements; + x = this.elements[0] - V[0]; + y = this.elements[1] - V[1]; + return Vector.create([ + V[0] + R[0][0] * x + R[0][1] * y, + V[1] + R[1][0] * x + R[1][1] * y + ]); + break; + case 3: + if (!obj.direction) { return null; } + var C = obj.pointClosestTo(this).elements; + R = Matrix.Rotation(t, obj.direction).elements; + x = this.elements[0] - C[0]; + y = this.elements[1] - C[1]; + z = this.elements[2] - C[2]; + return Vector.create([ + 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 + ]); + break; + default: + return null; + } + }, + + // Returns the result of reflecting the point in the given point, line or plane + reflectionIn: function(obj) { + if (obj.anchor) { + // obj is a plane or line + var P = this.elements.slice(); + var C = obj.pointClosestTo(P).elements; + return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]); + } else { + // obj is a point + var Q = obj.elements || obj; + if (this.elements.length != Q.length) { return null; } + return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); }); + } + }, + + // Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added + to3D: function() { + var V = this.dup(); + switch (V.elements.length) { + case 3: break; + case 2: V.elements.push(0); break; + default: return null; + } + return V; + }, + + // Returns a string representation of the vector + inspect: function() { + return '[' + this.elements.join(', ') + ']'; + }, + + // Set vector's elements from an array + setElements: function(els) { + this.elements = (els.elements || els).slice(); + return this; + } +}; + +// Constructor function +Vector.create = function(elements) { + var V = new Vector(); + return V.setElements(elements); +}; + +// i, j, k unit vectors +Vector.i = Vector.create([1,0,0]); +Vector.j = Vector.create([0,1,0]); +Vector.k = Vector.create([0,0,1]); + +// Random vector of size n +Vector.Random = function(n) { + var elements = []; + do { elements.push(Math.random()); + } while (--n); + return Vector.create(elements); +}; + +// Vector filled with zeros +Vector.Zero = function(n) { + var elements = []; + do { elements.push(0); + } while (--n); + return Vector.create(elements); +}; + + + +function Matrix() {} +Matrix.prototype = { + + // Returns element (i,j) of the matrix + e: function(i,j) { + if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; } + return this.elements[i-1][j-1]; + }, + + // Returns row k of the matrix as a vector + row: function(i) { + if (i > this.elements.length) { return null; } + return Vector.create(this.elements[i-1]); + }, + + // Returns column k of the matrix as a vector + col: function(j) { + if (j > this.elements[0].length) { return null; } + var col = [], n = this.elements.length, k = n, i; + do { i = k - n; + col.push(this.elements[i][j-1]); + } while (--n); + return Vector.create(col); + }, + + // Returns the number of rows/columns the matrix has + dimensions: function() { + return {rows: this.elements.length, cols: this.elements[0].length}; + }, + + // Returns the number of rows in the matrix + rows: function() { + return this.elements.length; + }, + + // Returns the number of columns in the matrix + cols: function() { + return this.elements[0].length; + }, + + // Returns true iff the matrix is equal to the argument. You can supply + // a vector as the argument, in which case the receiver must be a + // one-column matrix equal to the vector. + eql: function(matrix) { + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + if (this.elements.length != M.length || + this.elements[0].length != M[0].length) { return false; } + var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j; + do { i = ki - ni; + nj = kj; + do { j = kj - nj; + if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; } + } while (--nj); + } while (--ni); + return true; + }, + + // Returns a copy of the matrix + dup: function() { + return Matrix.create(this.elements); + }, + + // Maps the matrix to another matrix (of the same dimensions) according to the given function + map: function(fn) { + var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j; + do { i = ki - ni; + nj = kj; + els[i] = []; + do { j = kj - nj; + els[i][j] = fn(this.elements[i][j], i + 1, j + 1); + } while (--nj); + } while (--ni); + return Matrix.create(els); + }, + + // Returns true iff the argument has the same dimensions as the matrix + isSameSizeAs: function(matrix) { + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + return (this.elements.length == M.length && + this.elements[0].length == M[0].length); + }, + + // Returns the result of adding the argument to the matrix + add: function(matrix) { + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + if (!this.isSameSizeAs(M)) { return null; } + return this.map(function(x, i, j) { return x + M[i-1][j-1]; }); + }, + + // Returns the result of subtracting the argument from the matrix + subtract: function(matrix) { + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + if (!this.isSameSizeAs(M)) { return null; } + return this.map(function(x, i, j) { return x - M[i-1][j-1]; }); + }, + + // Returns true iff the matrix can multiply the argument from the left + canMultiplyFromLeft: function(matrix) { + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + // this.columns should equal matrix.rows + return (this.elements[0].length == M.length); + }, + + // Returns the result of multiplying the matrix from the right by the argument. + // If the argument is a scalar then just multiply all the elements. If the argument is + // a vector, a vector is returned, which saves you having to remember calling + // col(1) on the result. + multiply: function(matrix) { + if (!matrix.elements) { + return this.map(function(x) { return x * matrix; }); + } + var returnVector = matrix.modulus ? true : false; + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + if (!this.canMultiplyFromLeft(M)) { return null; } + var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j; + var cols = this.elements[0].length, elements = [], sum, nc, c; + do { i = ki - ni; + elements[i] = []; + nj = kj; + do { j = kj - nj; + sum = 0; + nc = cols; + do { c = cols - nc; + sum += this.elements[i][c] * M[c][j]; + } while (--nc); + elements[i][j] = sum; + } while (--nj); + } while (--ni); + var M = Matrix.create(elements); + return returnVector ? M.col(1) : M; + }, + + x: function(matrix) { return this.multiply(matrix); }, + + // Returns a submatrix taken from the matrix + // Argument order is: start row, start col, nrows, ncols + // Element selection wraps if the required index is outside the matrix's bounds, so you could + // use this to perform row/column cycling or copy-augmenting. + minor: function(a, b, c, d) { + var elements = [], ni = c, i, nj, j; + var rows = this.elements.length, cols = this.elements[0].length; + do { i = c - ni; + elements[i] = []; + nj = d; + do { j = d - nj; + elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols]; + } while (--nj); + } while (--ni); + return Matrix.create(elements); + }, + + // Returns the transpose of the matrix + transpose: function() { + var rows = this.elements.length, cols = this.elements[0].length; + var elements = [], ni = cols, i, nj, j; + do { i = cols - ni; + elements[i] = []; + nj = rows; + do { j = rows - nj; + elements[i][j] = this.elements[j][i]; + } while (--nj); + } while (--ni); + return Matrix.create(elements); + }, + + // Returns true iff the matrix is square + isSquare: function() { + return (this.elements.length == this.elements[0].length); + }, + + // Returns the (absolute) largest element of the matrix + max: function() { + var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j; + do { i = ki - ni; + nj = kj; + do { j = kj - nj; + if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; } + } while (--nj); + } while (--ni); + return m; + }, + + // Returns the indeces of the first match found by reading row-by-row from left to right + indexOf: function(x) { + var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j; + do { i = ki - ni; + nj = kj; + do { j = kj - nj; + if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; } + } while (--nj); + } while (--ni); + return null; + }, + + // If the matrix is square, returns the diagonal elements as a vector. + // Otherwise, returns null. + diagonal: function() { + if (!this.isSquare) { return null; } + var els = [], n = this.elements.length, k = n, i; + do { i = k - n; + els.push(this.elements[i][i]); + } while (--n); + return Vector.create(els); + }, + + // Make the matrix upper (right) triangular by Gaussian elimination. + // This method only adds multiples of rows to other rows. No rows are + // scaled up or switched, and the determinant is preserved. + toRightTriangular: function() { + var M = this.dup(), els; + var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p; + do { i = k - n; + if (M.elements[i][i] == 0) { + for (j = i + 1; j < k; j++) { + if (M.elements[j][i] != 0) { + els = []; np = kp; + do { p = kp - np; + els.push(M.elements[i][p] + M.elements[j][p]); + } while (--np); + M.elements[i] = els; + break; + } + } + } + if (M.elements[i][i] != 0) { + for (j = i + 1; j < k; j++) { + var multiplier = M.elements[j][i] / M.elements[i][i]; + els = []; np = kp; + do { p = kp - np; + // Elements with column numbers up to an including the number + // of the row that we're subtracting can safely be set straight to + // zero, since that's the point of this routine and it avoids having + // to loop over and correct rounding errors later + els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier); + } while (--np); + M.elements[j] = els; + } + } + } while (--n); + return M; + }, + + toUpperTriangular: function() { return this.toRightTriangular(); }, + + // Returns the determinant for square matrices + determinant: function() { + if (!this.isSquare()) { return null; } + var M = this.toRightTriangular(); + var det = M.elements[0][0], n = M.elements.length - 1, k = n, i; + do { i = k - n + 1; + det = det * M.elements[i][i]; + } while (--n); + return det; + }, + + det: function() { return this.determinant(); }, + + // Returns true iff the matrix is singular + isSingular: function() { + return (this.isSquare() && this.determinant() === 0); + }, + + // Returns the trace for square matrices + trace: function() { + if (!this.isSquare()) { return null; } + var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i; + do { i = k - n + 1; + tr += this.elements[i][i]; + } while (--n); + return tr; + }, + + tr: function() { return this.trace(); }, + + // Returns the rank of the matrix + rank: function() { + var M = this.toRightTriangular(), rank = 0; + var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j; + do { i = ki - ni; + nj = kj; + do { j = kj - nj; + if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; } + } while (--nj); + } while (--ni); + return rank; + }, + + rk: function() { return this.rank(); }, + + // Returns the result of attaching the given argument to the right-hand side of the matrix + augment: function(matrix) { + var M = matrix.elements || matrix; + if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; } + var T = this.dup(), cols = T.elements[0].length; + var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j; + if (ni != M.length) { return null; } + do { i = ki - ni; + nj = kj; + do { j = kj - nj; + T.elements[i][cols + j] = M[i][j]; + } while (--nj); + } while (--ni); + return T; + }, + + // Returns the inverse (if one exists) using Gauss-Jordan + inverse: function() { + if (!this.isSquare() || this.isSingular()) { return null; } + var ni = this.elements.length, ki = ni, i, j; + var M = this.augment(Matrix.I(ni)).toRightTriangular(); + var np, kp = M.elements[0].length, p, els, divisor; + var inverse_elements = [], new_element; + // Matrix is non-singular so there will be no zeros on the diagonal + // Cycle through rows from last to first + do { i = ni - 1; + // First, normalise diagonal elements to 1 + els = []; np = kp; + inverse_elements[i] = []; + divisor = M.elements[i][i]; + do { p = kp - np; + new_element = M.elements[i][p] / divisor; + els.push(new_element); + // Shuffle of the current row of the right hand side into the results + // array as it will not be modified by later runs through this loop + if (p >= ki) { inverse_elements[i].push(new_element); } + } while (--np); + M.elements[i] = els; + // Then, subtract this row from those above it to + // give the identity matrix on the left hand side + for (j = 0; j < i; j++) { + els = []; np = kp; + do { p = kp - np; + els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]); + } while (--np); + M.elements[j] = els; + } + } while (--ni); + return Matrix.create(inverse_elements); + }, + + inv: function() { return this.inverse(); }, + + // Returns the result of rounding all the elements + round: function() { + return this.map(function(x) { return Math.round(x); }); + }, + + // Returns a copy of the matrix with elements set to the given value if they + // differ from it by less than Sylvester.precision + snapTo: function(x) { + return this.map(function(p) { + return (Math.abs(p - x) <= Sylvester.precision) ? x : p; + }); + }, + + // Returns a string representation of the matrix + inspect: function() { + var matrix_rows = []; + var n = this.elements.length, k = n, i; + do { i = k - n; + matrix_rows.push(Vector.create(this.elements[i]).inspect()); + } while (--n); + return matrix_rows.join('\n'); + }, + + // Set the matrix's elements from an array. If the argument passed + // is a vector, the resulting matrix will be a single column. + setElements: function(els) { + var i, elements = els.elements || els; + if (typeof(elements[0][0]) != 'undefined') { + var ni = elements.length, ki = ni, nj, kj, j; + this.elements = []; + do { i = ki - ni; + nj = elements[i].length; kj = nj; + this.elements[i] = []; + do { j = kj - nj; + this.elements[i][j] = elements[i][j]; + } while (--nj); + } while(--ni); + return this; + } + var n = elements.length, k = n; + this.elements = []; + do { i = k - n; + this.elements.push([elements[i]]); + } while (--n); + return this; + } +}; + +// Constructor function +Matrix.create = function(elements) { + var M = new Matrix(); + return M.setElements(elements); +}; + +// Identity matrix of size n +Matrix.I = function(n) { + var els = [], k = n, i, nj, j; + do { i = k - n; + els[i] = []; nj = k; + do { j = k - nj; + els[i][j] = (i == j) ? 1 : 0; + } while (--nj); + } while (--n); + return Matrix.create(els); +}; + +// Diagonal matrix - all off-diagonal elements are zero +Matrix.Diagonal = function(elements) { + var n = elements.length, k = n, i; + var M = Matrix.I(n); + do { i = k - n; + M.elements[i][i] = elements[i]; + } while (--n); + return M; +}; + +// Rotation matrix about some axis. If no axis is +// supplied, assume we're after a 2D transform +Matrix.Rotation = function(theta, a) { + if (!a) { + return Matrix.create([ + [Math.cos(theta), -Math.sin(theta)], + [Math.sin(theta), Math.cos(theta)] + ]); + } + var axis = a.dup(); + if (axis.elements.length != 3) { return null; } + var mod = axis.modulus(); + var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod; + var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c; + // Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp + // That proof rotates the co-ordinate system so theta + // becomes -theta and sin becomes -sin here. + return Matrix.create([ + [ 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 ] + ]); +}; + +// Special case rotations +Matrix.RotationX = function(t) { + var c = Math.cos(t), s = Math.sin(t); + return Matrix.create([ + [ 1, 0, 0 ], + [ 0, c, -s ], + [ 0, s, c ] + ]); +}; +Matrix.RotationY = function(t) { + var c = Math.cos(t), s = Math.sin(t); + return Matrix.create([ + [ c, 0, s ], + [ 0, 1, 0 ], + [ -s, 0, c ] + ]); +}; +Matrix.RotationZ = function(t) { + var c = Math.cos(t), s = Math.sin(t); + return Matrix.create([ + [ c, -s, 0 ], + [ s, c, 0 ], + [ 0, 0, 1 ] + ]); +}; + +// Random matrix of n rows, m columns +Matrix.Random = function(n, m) { + return Matrix.Zero(n, m).map( + function() { return Math.random(); } + ); +}; + +// Matrix filled with zeros +Matrix.Zero = function(n, m) { + var els = [], ni = n, i, nj, j; + do { i = n - ni; + els[i] = []; + nj = m; + do { j = m - nj; + els[i][j] = 0; + } while (--nj); + } while (--ni); + return Matrix.create(els); +}; + + + +function Line() {} +Line.prototype = { + + // Returns true if the argument occupies the same space as the line + eql: function(line) { + return (this.isParallelTo(line) && this.contains(line.anchor)); + }, + + // Returns a copy of the line + dup: function() { + return Line.create(this.anchor, this.direction); + }, + + // Returns the result of translating the line by the given vector/array + translate: function(vector) { + var V = vector.elements || vector; + return Line.create([ + this.anchor.elements[0] + V[0], + this.anchor.elements[1] + V[1], + this.anchor.elements[2] + (V[2] || 0) + ], this.direction); + }, + + // Returns true if the line is parallel to the argument. Here, 'parallel to' + // means that the argument's direction is either parallel or antiparallel to + // the line's own direction. A line is parallel to a plane if the two do not + // have a unique intersection. + isParallelTo: function(obj) { + if (obj.normal) { return obj.isParallelTo(this); } + var theta = this.direction.angleFrom(obj.direction); + return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision); + }, + + // Returns the line's perpendicular distance from the argument, + // which can be a point, a line or a plane + distanceFrom: function(obj) { + if (obj.normal) { return obj.distanceFrom(this); } + if (obj.direction) { + // obj is a line + if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); } + var N = this.direction.cross(obj.direction).toUnitVector().elements; + var A = this.anchor.elements, B = obj.anchor.elements; + return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]); + } else { + // obj is a point + var P = obj.elements || obj; + var A = this.anchor.elements, D = this.direction.elements; + var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2]; + var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3); + if (modPA === 0) return 0; + // Assumes direction vector is normalized + var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA; + var sin2 = 1 - cosTheta*cosTheta; + return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2)); + } + }, + + // Returns true iff the argument is a point on the line + contains: function(point) { + var dist = this.distanceFrom(point); + return (dist !== null && dist <= Sylvester.precision); + }, + + // Returns true iff the line lies in the given plane + liesIn: function(plane) { + return plane.contains(this); + }, + + // Returns true iff the line has a unique point of intersection with the argument + intersects: function(obj) { + if (obj.normal) { return obj.intersects(this); } + return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision); + }, + + // Returns the unique intersection point with the argument, if one exists + intersectionWith: function(obj) { + if (obj.normal) { return obj.intersectionWith(this); } + if (!this.intersects(obj)) { return null; } + var P = this.anchor.elements, X = this.direction.elements, + Q = obj.anchor.elements, Y = obj.direction.elements; + var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2]; + var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2]; + var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3; + var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3; + var XdotX = X1*X1 + X2*X2 + X3*X3; + var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3; + var XdotY = X1*Y1 + X2*Y2 + X3*Y3; + var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY); + return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]); + }, + + // Returns the point on the line that is closest to the given point or line + pointClosestTo: function(obj) { + if (obj.direction) { + // obj is a line + if (this.intersects(obj)) { return this.intersectionWith(obj); } + if (this.isParallelTo(obj)) { return null; } + var D = this.direction.elements, E = obj.direction.elements; + var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2]; + // Create plane containing obj and the shared normal and intersect this with it + // Thank you: http://www.cgafaq.info/wiki/Line-line_distance + var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2); + var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]); + var P = Plane.create(obj.anchor, N); + return P.intersectionWith(this); + } else { + // obj is a point + var P = obj.elements || obj; + if (this.contains(P)) { return Vector.create(P); } + var A = this.anchor.elements, D = this.direction.elements; + var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2]; + var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2), + z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3); + var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]); + var k = this.distanceFrom(P) / V.modulus(); + return Vector.create([ + P[0] + V.elements[0] * k, + P[1] + V.elements[1] * k, + (P[2] || 0) + V.elements[2] * k + ]); + } + }, + + // Returns a copy of the line rotated by t radians about the given line. Works by + // finding the argument's closest point to this line's anchor point (call this C) and + // rotating the anchor about C. Also rotates the line's direction about the argument's. + // Be careful with this - the rotation axis' direction affects the outcome! + rotate: function(t, line) { + // If we're working in 2D + if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); } + var R = Matrix.Rotation(t, line.direction).elements; + var C = line.pointClosestTo(this.anchor).elements; + var A = this.anchor.elements, D = this.direction.elements; + var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2]; + var x = A1 - C1, y = A2 - C2, z = A3 - C3; + return Line.create([ + C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z, + C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z, + C3 + 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] + ]); + }, + + // Returns the line's reflection in the given point or line + reflectionIn: function(obj) { + if (obj.normal) { + // obj is a plane + var A = this.anchor.elements, D = this.direction.elements; + var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2]; + var newA = this.anchor.reflectionIn(obj).elements; + // Add the line's direction vector to its anchor, then mirror that in the plane + var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3; + var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements; + var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]]; + return Line.create(newA, newD); + } else if (obj.direction) { + // obj is a line - reflection obtained by rotating PI radians about obj + return this.rotate(Math.PI, obj); + } else { + // obj is a point - just reflect the line's anchor in it + var P = obj.elements || obj; + return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction); + } + }, + + // Set the line's anchor point and direction. + setVectors: function(anchor, direction) { + // Need to do this so that line's properties are not + // references to the arguments passed in + anchor = Vector.create(anchor); + direction = Vector.create(direction); + if (anchor.elements.length == 2) {anchor.elements.push(0); } + if (direction.elements.length == 2) { direction.elements.push(0); } + if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; } + var mod = direction.modulus(); + if (mod === 0) { return null; } + this.anchor = anchor; + this.direction = Vector.create([ + direction.elements[0] / mod, + direction.elements[1] / mod, + direction.elements[2] / mod + ]); + return this; + } +}; + + +// Constructor function +Line.create = function(anchor, direction) { + var L = new Line(); + return L.setVectors(anchor, direction); +}; + +// Axes +Line.X = Line.create(Vector.Zero(3), Vector.i); +Line.Y = Line.create(Vector.Zero(3), Vector.j); +Line.Z = Line.create(Vector.Zero(3), Vector.k); + + + +function Plane() {} +Plane.prototype = { + + // Returns true iff the plane occupies the same space as the argument + eql: function(plane) { + return (this.contains(plane.anchor) && this.isParallelTo(plane)); + }, + + // Returns a copy of the plane + dup: function() { + return Plane.create(this.anchor, this.normal); + }, + + // Returns the result of translating the plane by the given vector + translate: function(vector) { + var V = vector.elements || vector; + return Plane.create([ + this.anchor.elements[0] + V[0], + this.anchor.elements[1] + V[1], + this.anchor.elements[2] + (V[2] || 0) + ], this.normal); + }, + + // Returns true iff the plane is parallel to the argument. Will return true + // if the planes are equal, or if you give a line and it lies in the plane. + isParallelTo: function(obj) { + var theta; + if (obj.normal) { + // obj is a plane + theta = this.normal.angleFrom(obj.normal); + return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision); + } else if (obj.direction) { + // obj is a line + return this.normal.isPerpendicularTo(obj.direction); + } + return null; + }, + + // Returns true iff the receiver is perpendicular to the argument + isPerpendicularTo: function(plane) { + var theta = this.normal.angleFrom(plane.normal); + return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision); + }, + + // Returns the plane's distance from the given object (point, line or plane) + distanceFrom: function(obj) { + if (this.intersects(obj) || this.contains(obj)) { return 0; } + if (obj.anchor) { + // obj is a plane or line + var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements; + return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]); + } else { + // obj is a point + var P = obj.elements || obj; + var A = this.anchor.elements, N = this.normal.elements; + return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]); + } + }, + + // Returns true iff the plane contains the given point or line + contains: function(obj) { + if (obj.normal) { return null; } + if (obj.direction) { + return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction))); + } else { + var P = obj.elements || obj; + var A = this.anchor.elements, N = this.normal.elements; + var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0))); + return (diff <= Sylvester.precision); + } + }, + + // Returns true iff the plane has a unique point/line of intersection with the argument + intersects: function(obj) { + if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; } + return !this.isParallelTo(obj); + }, + + // Returns the unique intersection with the argument, if one exists. The result + // will be a vector if a line is supplied, and a line if a plane is supplied. + intersectionWith: function(obj) { + if (!this.intersects(obj)) { return null; } + if (obj.direction) { + // obj is a line + var A = obj.anchor.elements, D = obj.direction.elements, + P = this.anchor.elements, N = this.normal.elements; + 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]); + return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]); + } else if (obj.normal) { + // obj is a plane + var direction = this.normal.cross(obj.normal).toUnitVector(); + // To find an anchor point, we find one co-ordinate that has a value + // of zero somewhere on the intersection, and remember which one we picked + var N = this.normal.elements, A = this.anchor.elements, + O = obj.normal.elements, B = obj.anchor.elements; + var solver = Matrix.Zero(2,2), i = 0; + while (solver.isSingular()) { + i++; + solver = Matrix.create([ + [ N[i%3], N[(i+1)%3] ], + [ O[i%3], O[(i+1)%3] ] + ]); + } + // Then we solve the simultaneous equations in the remaining dimensions + var inverse = solver.inverse().elements; + var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2]; + var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2]; + var intersection = [ + inverse[0][0] * x + inverse[0][1] * y, + inverse[1][0] * x + inverse[1][1] * y + ]; + var anchor = []; + for (var j = 1; j <= 3; j++) { + // This formula picks the right element from intersection by + // cycling depending on which element we set to zero above + anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]); + } + return Line.create(anchor, direction); + } + }, + + // Returns the point in the plane closest to the given point + pointClosestTo: function(point) { + var P = point.elements || point; + var A = this.anchor.elements, N = this.normal.elements; + var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]; + return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]); + }, + + // Returns a copy of the plane, rotated by t radians about the given line + // See notes on Line#rotate. + rotate: function(t, line) { + var R = Matrix.Rotation(t, line.direction).elements; + var C = line.pointClosestTo(this.anchor).elements; + var A = this.anchor.elements, N = this.normal.elements; + var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2]; + var x = A1 - C1, y = A2 - C2, z = A3 - C3; + return Plane.create([ + C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z, + C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z, + C3 + 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] + ]); + }, + + // Returns the reflection of the plane in the given point, line or plane. + reflectionIn: function(obj) { + if (obj.normal) { + // obj is a plane + var A = this.anchor.elements, N = this.normal.elements; + var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2]; + var newA = this.anchor.reflectionIn(obj).elements; + // Add the plane's normal to its anchor, then mirror that in the other plane + var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3; + var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements; + var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]]; + return Plane.create(newA, newN); + } else if (obj.direction) { + // obj is a line + return this.rotate(Math.PI, obj); + } else { + // obj is a point + var P = obj.elements || obj; + return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal); + } + }, + + // Sets the anchor point and normal to the plane. If three arguments are specified, + // the normal is calculated by assuming the three points should lie in the same plane. + // If only two are sepcified, the second is taken to be the normal. Normal vector is + // normalised before storage. + setVectors: function(anchor, v1, v2) { + anchor = Vector.create(anchor); + anchor = anchor.to3D(); if (anchor === null) { return null; } + v1 = Vector.create(v1); + v1 = v1.to3D(); if (v1 === null) { return null; } + if (typeof(v2) == 'undefined') { + v2 = null; + } else { + v2 = Vector.create(v2); + v2 = v2.to3D(); if (v2 === null) { return null; } + } + var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2]; + var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2]; + var normal, mod; + if (v2 !== null) { + var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2]; + normal = Vector.create([ + (v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2), + (v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3), + (v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1) + ]); + mod = normal.modulus(); + if (mod === 0) { return null; } + normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]); + } else { + mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13); + if (mod === 0) { return null; } + normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]); + } + this.anchor = anchor; + this.normal = normal; + return this; + } +}; + +// Constructor function +Plane.create = function(anchor, v1, v2) { + var P = new Plane(); + return P.setVectors(anchor, v1, v2); +}; + +// X-Y-Z planes +Plane.XY = Plane.create(Vector.Zero(3), Vector.k); +Plane.YZ = Plane.create(Vector.Zero(3), Vector.i); +Plane.ZX = Plane.create(Vector.Zero(3), Vector.j); +Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX; + +// Utility functions +var $V = Vector.create; +var $M = Matrix.create; +var $L = Line.create; +var $P = Plane.create; diff --git a/src/axis.cpp b/src/axis.cpp index 7a40b24..c0a81e5 100644 --- a/src/axis.cpp +++ b/src/axis.cpp @@ -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;iaa.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 { diff --git a/src/base.cpp b/src/base.cpp index df7c74b..6e9660e 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -316,7 +316,7 @@ bool mglBase::RecalcCRange() { FMin.c = Min.c; FMax.c = Max.c; } else { - FMin.c = 1e30; FMax.c = -1e30; + FMin.c = INFINITY; FMax.c = -INFINITY; register int i; mreal a; int n=30; @@ -339,8 +339,8 @@ void mglBase::RecalcBorder() { FMin = Min; FMax = Max; } else { - FMin = mglPoint( 1e30, 1e30, 1e30); - FMax = mglPoint(-1e30,-1e30,-1e30); + FMin = mglPoint( INFINITY, INFINITY, INFINITY); + FMax = mglPoint(-INFINITY,-INFINITY,-INFINITY); register int i,j; int n=30; for(i=0;i<=n;i++) for(j=0;j<=n;j++) // x range diff --git a/src/data.cpp b/src/data.cpp index 3481a07..b886cd8 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -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 for(long k=0;kGetNN(); const mglData *b = dynamic_cast(d); #pragma omp parallel { - register mreal m=-1e10, v; + register mreal m=-INFINITY, v; if(b) #pragma omp for nowait for(long i=0;iGetNN(); const mglData *b = dynamic_cast(d); #pragma omp parallel { - register mreal m=0, v; + register mreal m=INFINITY, v; if(b) #pragma omp for nowait for(long i=0;iGetNN(); const mglData *b = dynamic_cast(d); #pragma omp parallel { - register mreal m=1e10, v; + register mreal m=INFINITY, v; if(b) #pragma omp for nowait for(long i=0;iGetNx(), ny=d->GetNy(), nn=d->GetNN(); #pragma omp parallel { - register mreal m=-1e10, v; + register mreal m=-INFINITY, v; long im=-1,jm=-1,km=-1; #pragma omp for nowait for(long ii=0;iiGetNx(), ny=d->GetNy(), nn=d->GetNN(); #pragma omp parallel { - register mreal m=1e10, v; + register mreal m=INFINITY, v; long im=-1,jm=-1,km=-1; #pragma omp for nowait for(long ii=0;iinx*d->ny*d->nz; - mreal a1=1e20,a2=-1e20,v,*a=d->a; + mreal a1=INFINITY,a2=-INFINITY,v,*a=d->a; s = dim*d->ny*(d->nz>1 ? d->nx : 1); for(long i=s;ia[i] ? a[i]:a1; a2 = a2=nz) ns=0; if(v1==v2) { - v1 = 1e20; v2=-1e20; + v1 = INFINITY; v2=-INFINITY; if(md) //#pragma omp parallel for // NOTE comparison here for(long i=0;iSetWarn(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 diff --git a/src/vect.cpp b/src/vect.cpp index 4644562..5b46c57 100644 --- a/src/vect.cpp +++ b/src/vect.cpp @@ -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'); // find coordinates u, v register long i,j; - register mreal d, dm=1e7; + register mreal d, dm=INFINITY; long i0=0,j0=0; for(i=0;i +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 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 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. diff --git a/texinfo/example_ru.texi b/texinfo/example_ru.texi index 6b765c7..f6001f1 100644 --- a/texinfo/example_ru.texi +++ b/texinfo/example_ru.texi @@ -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 +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 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 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 -#include - -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 -//----------------------------------------------------------------------------- -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 написан М.Видассовым. diff --git a/texinfo/version.texi b/texinfo/version.texi index a53255e..3b57693 100644 --- a/texinfo/version.texi +++ b/texinfo/version.texi @@ -1,3 +1,3 @@ @set VERSION 2.2 -@set MINVER .1 +@set MINVER .2 @c @set MINVER diff --git a/texinfo/version_hist.txt b/texinfo/version_hist.txt index fa59be6..29c5289 100644 --- a/texinfo/version_hist.txt +++ b/texinfo/version_hist.txt @@ -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 diff --git a/texinfo/web_en.texi b/texinfo/web_en.texi index 51f5b64..4a39ffa 100644 --- a/texinfo/web_en.texi +++ b/texinfo/web_en.texi @@ -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: diff --git a/texinfo/web_ru.texi b/texinfo/web_ru.texi index 51f5b64..4a39ffa 100644 --- a/texinfo/web_ru.texi +++ b/texinfo/web_ru.texi @@ -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: diff --git a/todo.txt b/todo.txt index d3da54c..398fa4b 100644 --- a/todo.txt +++ b/todo.txt @@ -20,17 +20,18 @@ 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 instead of std::vector 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 instead of std::vector 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 ============= diff --git a/widgets/glut.cpp b/widgets/glut.cpp index 2e18075..2ecd8c0 100644 --- a/widgets/glut.cpp +++ b/widgets/glut.cpp @@ -211,7 +211,7 @@ void mglCanvasGLUT::Window(int argc, char **argv,int (*draw)(mglBase *gr, void * delete []tmp[0]; glutInitDisplayMode(GLUT_RGB); glutInitWindowSize(600, 600); - glutCreateWindow("MathPlotLibrary"); + glutCreateWindow("MathGL"); AddLight(0,mglPoint(0,0,3),false); if(draw) @@ -231,7 +231,7 @@ void mglCanvasGLUT::Window(int argc, char **argv,int (*draw)(mglBase *gr, void * // TODO Add window maximazing at start up ??? glutMainLoop(); - glDeleteLists(1,NumFig); + if(NumFig>0) glDeleteLists(1,NumFig); } //----------------------------------------------------------------------------- HMGL MGL_EXPORT mgl_create_graph_glut(int (*draw)(HMGL gr, void *p), const char *title, void *par, void (*load)(void *p))