summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/media9/javascript
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/media9/javascript
Initial commit
Diffstat (limited to 'macros/latex/contrib/media9/javascript')
-rw-r--r--macros/latex/contrib/media9/javascript/3Dmenu.js854
-rw-r--r--macros/latex/contrib/media9/javascript/3Dspintool.js29
-rw-r--r--macros/latex/contrib/media9/javascript/animation.js116
-rw-r--r--macros/latex/contrib/media9/javascript/asylabels.js85
4 files changed, 1084 insertions, 0 deletions
diff --git a/macros/latex/contrib/media9/javascript/3Dmenu.js b/macros/latex/contrib/media9/javascript/3Dmenu.js
new file mode 100644
index 0000000000..9037d040dd
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/3Dmenu.js
@@ -0,0 +1,854 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012--today, Alexander Grahn
+//
+// 3Dmenu.js
+//
+// version 20140923
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 3D JavaScript used by media9.sty
+//
+// Extended functionality of the (right click) context menu of 3D annotations.
+//
+// 1.) Adds the following items to the 3D context menu:
+//
+// * `Generate Default View'
+//
+// Finds good default camera settings, returned as options for use with
+// the \includemedia command.
+//
+// * `Get Current View'
+//
+// Determines camera, cross section and part settings of the current view,
+// returned as `VIEW' section that can be copied into a views file of
+// additional views. The views file is inserted using the `3Dviews' option
+// of \includemedia.
+//
+// * `Cross Section'
+//
+// Toggle switch to add or remove a cross section into or from the current
+// view. The cross section can be moved in the x, y, z directions using x,
+// y, z and X, Y, Z keys on the keyboard, be tilted against and spun
+// around the upright Z axis using the Up/Down and Left/Right arrow keys
+// and caled using the s and S keys.
+//
+// 2.) Enables manipulation of position and orientation of indiviual parts and
+// groups of parts in the 3D scene. Parts which have been selected with the
+// mouse can be scaled moved around and rotated like the cross section as
+// described above. To spin the parts around their local up-axis, keep
+// Control key pressed while using the Up/Down and Left/Right arrow keys.
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+// The code borrows heavily from Bernd Gaertners `Miniball' software,
+// originally written in C++, for computing the smallest enclosing ball of a
+// set of points; see: http://www.inf.ethz.ch/personal/gaertner/miniball.html
+//
+////////////////////////////////////////////////////////////////////////////////
+//host.console.show();
+
+//constructor for doubly linked list
+function List(){
+ this.first_node=null;
+ this.last_node=new Node(undefined);
+}
+List.prototype.push_back=function(x){
+ var new_node=new Node(x);
+ if(this.first_node==null){
+ this.first_node=new_node;
+ new_node.prev=null;
+ }else{
+ new_node.prev=this.last_node.prev;
+ new_node.prev.next=new_node;
+ }
+ new_node.next=this.last_node;
+ this.last_node.prev=new_node;
+};
+List.prototype.move_to_front=function(it){
+ var node=it.get();
+ if(node.next!=null && node.prev!=null){
+ node.next.prev=node.prev;
+ node.prev.next=node.next;
+ node.prev=null;
+ node.next=this.first_node;
+ this.first_node.prev=node;
+ this.first_node=node;
+ }
+};
+List.prototype.begin=function(){
+ var i=new Iterator();
+ i.target=this.first_node;
+ return(i);
+};
+List.prototype.end=function(){
+ var i=new Iterator();
+ i.target=this.last_node;
+ return(i);
+};
+function Iterator(it){
+ if( it!=undefined ){
+ this.target=it.target;
+ }else {
+ this.target=null;
+ }
+}
+Iterator.prototype.set=function(it){this.target=it.target;};
+Iterator.prototype.get=function(){return(this.target);};
+Iterator.prototype.deref=function(){return(this.target.data);};
+Iterator.prototype.incr=function(){
+ if(this.target.next!=null) this.target=this.target.next;
+};
+//constructor for node objects that populate the linked list
+function Node(x){
+ this.prev=null;
+ this.next=null;
+ this.data=x;
+}
+function sqr(r){return(r*r);}//helper function
+
+//Miniball algorithm by B. Gaertner
+function Basis(){
+ this.m=0;
+ this.q0=new Array(3);
+ this.z=new Array(4);
+ this.f=new Array(4);
+ this.v=new Array(new Array(3), new Array(3), new Array(3), new Array(3));
+ this.a=new Array(new Array(3), new Array(3), new Array(3), new Array(3));
+ this.c=new Array(new Array(3), new Array(3), new Array(3), new Array(3));
+ this.sqr_r=new Array(4);
+ this.current_c=this.c[0];
+ this.current_sqr_r=0;
+ this.reset();
+}
+Basis.prototype.center=function(){return(this.current_c);};
+Basis.prototype.size=function(){return(this.m);};
+Basis.prototype.pop=function(){--this.m;};
+Basis.prototype.excess=function(p){
+ var e=-this.current_sqr_r;
+ for(var k=0;k<3;++k){
+ e+=sqr(p[k]-this.current_c[k]);
+ }
+ return(e);
+};
+Basis.prototype.reset=function(){
+ this.m=0;
+ for(var j=0;j<3;++j){
+ this.c[0][j]=0;
+ }
+ this.current_c=this.c[0];
+ this.current_sqr_r=-1;
+};
+Basis.prototype.push=function(p){
+ var i, j;
+ var eps=1e-32;
+ if(this.m==0){
+ for(i=0;i<3;++i){
+ this.q0[i]=p[i];
+ }
+ for(i=0;i<3;++i){
+ this.c[0][i]=this.q0[i];
+ }
+ this.sqr_r[0]=0;
+ }else {
+ for(i=0;i<3;++i){
+ this.v[this.m][i]=p[i]-this.q0[i];
+ }
+ for(i=1;i<this.m;++i){
+ this.a[this.m][i]=0;
+ for(j=0;j<3;++j){
+ this.a[this.m][i]+=this.v[i][j]*this.v[this.m][j];
+ }
+ this.a[this.m][i]*=(2/this.z[i]);
+ }
+ for(i=1;i<this.m;++i){
+ for(j=0;j<3;++j){
+ this.v[this.m][j]-=this.a[this.m][i]*this.v[i][j];
+ }
+ }
+ this.z[this.m]=0;
+ for(j=0;j<3;++j){
+ this.z[this.m]+=sqr(this.v[this.m][j]);
+ }
+ this.z[this.m]*=2;
+ if(this.z[this.m]<eps*this.current_sqr_r) return(false);
+ var e=-this.sqr_r[this.m-1];
+ for(i=0;i<3;++i){
+ e+=sqr(p[i]-this.c[this.m-1][i]);
+ }
+ this.f[this.m]=e/this.z[this.m];
+ for(i=0;i<3;++i){
+ this.c[this.m][i]=this.c[this.m-1][i]+this.f[this.m]*this.v[this.m][i];
+ }
+ this.sqr_r[this.m]=this.sqr_r[this.m-1]+e*this.f[this.m]/2;
+ }
+ this.current_c=this.c[this.m];
+ this.current_sqr_r=this.sqr_r[this.m];
+ ++this.m;
+ return(true);
+};
+function Miniball(){
+ this.L=new List();
+ this.B=new Basis();
+ this.support_end=new Iterator();
+}
+Miniball.prototype.mtf_mb=function(it){
+ var i=new Iterator(it);
+ this.support_end.set(this.L.begin());
+ if((this.B.size())==4) return;
+ for(var k=new Iterator(this.L.begin());k.get()!=i.get();){
+ var j=new Iterator(k);
+ k.incr();
+ if(this.B.excess(j.deref()) > 0){
+ if(this.B.push(j.deref())){
+ this.mtf_mb(j);
+ this.B.pop();
+ if(this.support_end.get()==j.get())
+ this.support_end.incr();
+ this.L.move_to_front(j);
+ }
+ }
+ }
+};
+Miniball.prototype.check_in=function(b){
+ this.L.push_back(b);
+};
+Miniball.prototype.build=function(){
+ this.B.reset();
+ this.support_end.set(this.L.begin());
+ this.mtf_mb(this.L.end());
+};
+Miniball.prototype.center=function(){
+ return(this.B.center());
+};
+Miniball.prototype.radius=function(){
+ return(Math.sqrt(this.B.current_sqr_r));
+};
+
+//functions called by menu items
+function calc3Dopts () {
+ //create Miniball object
+ var mb=new Miniball();
+ //auxiliary vector
+ var corner=new Vector3();
+ //iterate over all visible mesh nodes in the scene
+ for(i=0;i<scene.meshes.count;i++){
+ var mesh=scene.meshes.getByIndex(i);
+ if(!mesh.visible) continue;
+ //local to parent transformation matrix
+ var trans=mesh.transform;
+ //build local to world transformation matrix by recursively
+ //multiplying the parent's transf. matrix on the right
+ var parent=mesh.parent;
+ while(parent.transform){
+ trans=trans.multiply(parent.transform);
+ parent=parent.parent;
+ }
+ //get the bbox of the mesh (local coordinates)
+ var bbox=mesh.computeBoundingBox();
+ //transform the local bounding box corner coordinates to
+ //world coordinates for bounding sphere determination
+ //BBox.min
+ corner.set(bbox.min);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ //BBox.max
+ corner.set(bbox.max);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ //remaining six BBox corners
+ corner.set(bbox.min.x, bbox.max.y, bbox.max.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.min.x, bbox.min.y, bbox.max.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.min.x, bbox.max.y, bbox.min.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.max.x, bbox.min.y, bbox.min.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.max.x, bbox.min.y, bbox.max.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ corner.set(bbox.max.x, bbox.max.y, bbox.min.z);
+ corner.set(trans.transformPosition(corner));
+ mb.check_in(new Array(corner.x, corner.y, corner.z));
+ }
+ //compute the smallest enclosing bounding sphere
+ mb.build();
+ //
+ //current camera settings
+ //
+ var camera=scene.cameras.getByIndex(0);
+ var res=''; //initialize result string
+ //aperture angle of the virtual camera (perspective projection) *or*
+ //orthographic scale (orthographic projection)
+ if(camera.projectionType==camera.TYPE_PERSPECTIVE){
+ var aac=camera.fov*180/Math.PI;
+ if(host.util.printf('%.4f', aac)!=30)
+ res+=host.util.printf('\n3Daac=%s,', aac);
+ }else{
+ camera.viewPlaneSize=2.*mb.radius();
+ res+=host.util.printf('\n3Dortho=%s,', 1./camera.viewPlaneSize);
+ }
+ //camera roll
+ var roll = camera.roll*180/Math.PI;
+ if(host.util.printf('%.4f', roll)!=0)
+ res+=host.util.printf('\n3Droll=%s,',roll);
+ //target to camera vector
+ var c2c=new Vector3();
+ c2c.set(camera.position);
+ c2c.subtractInPlace(camera.targetPosition);
+ c2c.normalize();
+ if(!(c2c.x==0 && c2c.y==-1 && c2c.z==0))
+ res+=host.util.printf('\n3Dc2c=%s %s %s,', c2c.x, c2c.y, c2c.z);
+ //
+ //new camera settings
+ //
+ //bounding sphere centre --> new camera target
+ var coo=new Vector3();
+ coo.set((mb.center())[0], (mb.center())[1], (mb.center())[2]);
+ if(coo.length)
+ res+=host.util.printf('\n3Dcoo=%s %s %s,', coo.x, coo.y, coo.z);
+ //radius of orbit
+ if(camera.projectionType==camera.TYPE_PERSPECTIVE){
+ var roo=mb.radius()/ Math.sin(aac * Math.PI/ 360.);
+ }else{
+ //orthographic projection
+ var roo=mb.radius();
+ }
+ res+=host.util.printf('\n3Droo=%s,', roo);
+ //update camera settings in the viewer
+ var currol=camera.roll;
+ camera.targetPosition.set(coo);
+ camera.position.set(coo.add(c2c.scale(roo)));
+ camera.roll=currol;
+ //determine background colour
+ rgb=scene.background.getColor();
+ if(!(rgb.r==1 && rgb.g==1 && rgb.b==1))
+ res+=host.util.printf('\n3Dbg=%s %s %s,', rgb.r, rgb.g, rgb.b);
+ //determine lighting scheme
+ switch(scene.lightScheme){
+ case scene.LIGHT_MODE_FILE:
+ curlights='Artwork';break;
+ case scene.LIGHT_MODE_NONE:
+ curlights='None';break;
+ case scene.LIGHT_MODE_WHITE:
+ curlights='White';break;
+ case scene.LIGHT_MODE_DAY:
+ curlights='Day';break;
+ case scene.LIGHT_MODE_NIGHT:
+ curlights='Night';break;
+ case scene.LIGHT_MODE_BRIGHT:
+ curlights='Hard';break;
+ case scene.LIGHT_MODE_RGB:
+ curlights='Primary';break;
+ case scene.LIGHT_MODE_BLUE:
+ curlights='Blue';break;
+ case scene.LIGHT_MODE_RED:
+ curlights='Red';break;
+ case scene.LIGHT_MODE_CUBE:
+ curlights='Cube';break;
+ case scene.LIGHT_MODE_CAD:
+ curlights='CAD';break;
+ case scene.LIGHT_MODE_HEADLAMP:
+ curlights='Headlamp';break;
+ }
+ if(curlights!='Artwork')
+ res+=host.util.printf('\n3Dlights=%s,', curlights);
+ //determine global render mode
+ switch(scene.renderMode){
+ case scene.RENDER_MODE_BOUNDING_BOX:
+ currender='BoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX:
+ currender='TransparentBoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX_OUTLINE:
+ currender='TransparentBoundingBoxOutline';break;
+ case scene.RENDER_MODE_VERTICES:
+ currender='Vertices';break;
+ case scene.RENDER_MODE_SHADED_VERTICES:
+ currender='ShadedVertices';break;
+ case scene.RENDER_MODE_WIREFRAME:
+ currender='Wireframe';break;
+ case scene.RENDER_MODE_SHADED_WIREFRAME:
+ currender='ShadedWireframe';break;
+ case scene.RENDER_MODE_SOLID:
+ currender='Solid';break;
+ case scene.RENDER_MODE_TRANSPARENT:
+ currender='Transparent';break;
+ case scene.RENDER_MODE_SOLID_WIREFRAME:
+ currender='SolidWireframe';break;
+ case scene.RENDER_MODE_TRANSPARENT_WIREFRAME:
+ currender='TransparentWireframe';break;
+ case scene.RENDER_MODE_ILLUSTRATION:
+ currender='Illustration';break;
+ case scene.RENDER_MODE_SOLID_OUTLINE:
+ currender='SolidOutline';break;
+ case scene.RENDER_MODE_SHADED_ILLUSTRATION:
+ currender='ShadedIllustration';break;
+ case scene.RENDER_MODE_HIDDEN_WIREFRAME:
+ currender='HiddenWireframe';break;
+ }
+ if(currender!='Solid')
+ res+=host.util.printf('\n3Drender=%s,', currender);
+ //write result string to the console
+ host.console.show();
+// host.console.clear();
+ host.console.println('%%\n%% Copy and paste the following text to the\n'+
+ '%% option list of \\includemedia!\n%%' + res + '\n');
+}
+
+function get3Dview () {
+ var camera=scene.cameras.getByIndex(0);
+ var coo=camera.targetPosition;
+ var c2c=camera.position.subtract(coo);
+ var roo=c2c.length;
+ c2c.normalize();
+ var res='VIEW%=insert optional name here\n';
+ if(!(coo.x==0 && coo.y==0 && coo.z==0))
+ res+=host.util.printf(' COO=%s %s %s\n', coo.x, coo.y, coo.z);
+ if(!(c2c.x==0 && c2c.y==-1 && c2c.z==0))
+ res+=host.util.printf(' C2C=%s %s %s\n', c2c.x, c2c.y, c2c.z);
+ if(roo > 1e-9)
+ res+=host.util.printf(' ROO=%s\n', roo);
+ var roll = camera.roll*180/Math.PI;
+ if(host.util.printf('%.4f', roll)!=0)
+ res+=host.util.printf(' ROLL=%s\n', roll);
+ if(camera.projectionType==camera.TYPE_PERSPECTIVE){
+ var aac=camera.fov * 180/Math.PI;
+ if(host.util.printf('%.4f', aac)!=30)
+ res+=host.util.printf(' AAC=%s\n', aac);
+ }else{
+ if(host.util.printf('%.4f', camera.viewPlaneSize)!=1)
+ res+=host.util.printf(' ORTHO=%s\n', 1./camera.viewPlaneSize);
+ }
+ rgb=scene.background.getColor();
+ if(!(rgb.r==1 && rgb.g==1 && rgb.b==1))
+ res+=host.util.printf(' BGCOLOR=%s %s %s\n', rgb.r, rgb.g, rgb.b);
+ switch(scene.lightScheme){
+ case scene.LIGHT_MODE_FILE:
+ curlights='Artwork';break;
+ case scene.LIGHT_MODE_NONE:
+ curlights='None';break;
+ case scene.LIGHT_MODE_WHITE:
+ curlights='White';break;
+ case scene.LIGHT_MODE_DAY:
+ curlights='Day';break;
+ case scene.LIGHT_MODE_NIGHT:
+ curlights='Night';break;
+ case scene.LIGHT_MODE_BRIGHT:
+ curlights='Hard';break;
+ case scene.LIGHT_MODE_RGB:
+ curlights='Primary';break;
+ case scene.LIGHT_MODE_BLUE:
+ curlights='Blue';break;
+ case scene.LIGHT_MODE_RED:
+ curlights='Red';break;
+ case scene.LIGHT_MODE_CUBE:
+ curlights='Cube';break;
+ case scene.LIGHT_MODE_CAD:
+ curlights='CAD';break;
+ case scene.LIGHT_MODE_HEADLAMP:
+ curlights='Headlamp';break;
+ }
+ if(curlights!='Artwork')
+ res+=' LIGHTS='+curlights+'\n';
+ switch(scene.renderMode){
+ case scene.RENDER_MODE_BOUNDING_BOX:
+ defaultrender='BoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX:
+ defaultrender='TransparentBoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX_OUTLINE:
+ defaultrender='TransparentBoundingBoxOutline';break;
+ case scene.RENDER_MODE_VERTICES:
+ defaultrender='Vertices';break;
+ case scene.RENDER_MODE_SHADED_VERTICES:
+ defaultrender='ShadedVertices';break;
+ case scene.RENDER_MODE_WIREFRAME:
+ defaultrender='Wireframe';break;
+ case scene.RENDER_MODE_SHADED_WIREFRAME:
+ defaultrender='ShadedWireframe';break;
+ case scene.RENDER_MODE_SOLID:
+ defaultrender='Solid';break;
+ case scene.RENDER_MODE_TRANSPARENT:
+ defaultrender='Transparent';break;
+ case scene.RENDER_MODE_SOLID_WIREFRAME:
+ defaultrender='SolidWireframe';break;
+ case scene.RENDER_MODE_TRANSPARENT_WIREFRAME:
+ defaultrender='TransparentWireframe';break;
+ case scene.RENDER_MODE_ILLUSTRATION:
+ defaultrender='Illustration';break;
+ case scene.RENDER_MODE_SOLID_OUTLINE:
+ defaultrender='SolidOutline';break;
+ case scene.RENDER_MODE_SHADED_ILLUSTRATION:
+ defaultrender='ShadedIllustration';break;
+ case scene.RENDER_MODE_HIDDEN_WIREFRAME:
+ defaultrender='HiddenWireframe';break;
+ }
+ if(defaultrender!='Solid')
+ res+=' RENDERMODE='+defaultrender+'\n';
+
+ //detect existing Clipping Plane (3D Cross Section)
+ var clip=null;
+ if(
+ clip=scene.nodes.getByName('$$$$$$')||
+ clip=scene.nodes.getByName('Clipping Plane')
+ );
+ for(var i=0;i<scene.nodes.count;i++){
+ var nd=scene.nodes.getByIndex(i);
+ if(nd==clip||nd.name=='') continue;
+ var ndUTFName='';
+ for (var j=0; j<nd.name.length; j++) {
+ var theUnicode = nd.name.charCodeAt(j).toString(16);
+ while (theUnicode.length<4) theUnicode = '0' + theUnicode;
+ ndUTFName += theUnicode;
+ }
+ var end=nd.name.lastIndexOf('.');
+ if(end>0) var ndUserName=nd.name.substr(0,end);
+ else var ndUserName=nd.name;
+ respart=' PART='+ndUserName+'\n';
+ respart+=' UTF16NAME='+ndUTFName+'\n';
+ defaultvals=true;
+ if(!nd.visible){
+ respart+=' VISIBLE=false\n';
+ defaultvals=false;
+ }
+ if(nd.opacity<1.0){
+ respart+=' OPACITY='+nd.opacity+'\n';
+ defaultvals=false;
+ }
+ if(nd.constructor.name=='Mesh'){
+ currender=defaultrender;
+ switch(nd.renderMode){
+ case scene.RENDER_MODE_BOUNDING_BOX:
+ currender='BoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX:
+ currender='TransparentBoundingBox';break;
+ case scene.RENDER_MODE_TRANSPARENT_BOUNDING_BOX_OUTLINE:
+ currender='TransparentBoundingBoxOutline';break;
+ case scene.RENDER_MODE_VERTICES:
+ currender='Vertices';break;
+ case scene.RENDER_MODE_SHADED_VERTICES:
+ currender='ShadedVertices';break;
+ case scene.RENDER_MODE_WIREFRAME:
+ currender='Wireframe';break;
+ case scene.RENDER_MODE_SHADED_WIREFRAME:
+ currender='ShadedWireframe';break;
+ case scene.RENDER_MODE_SOLID:
+ currender='Solid';break;
+ case scene.RENDER_MODE_TRANSPARENT:
+ currender='Transparent';break;
+ case scene.RENDER_MODE_SOLID_WIREFRAME:
+ currender='SolidWireframe';break;
+ case scene.RENDER_MODE_TRANSPARENT_WIREFRAME:
+ currender='TransparentWireframe';break;
+ case scene.RENDER_MODE_ILLUSTRATION:
+ currender='Illustration';break;
+ case scene.RENDER_MODE_SOLID_OUTLINE:
+ currender='SolidOutline';break;
+ case scene.RENDER_MODE_SHADED_ILLUSTRATION:
+ currender='ShadedIllustration';break;
+ case scene.RENDER_MODE_HIDDEN_WIREFRAME:
+ currender='HiddenWireframe';break;
+ //case scene.RENDER_MODE_DEFAULT:
+ // currender='Default';break;
+ }
+ if(currender!=defaultrender){
+ respart+=' RENDERMODE='+currender+'\n';
+ defaultvals=false;
+ }
+ }
+ if(origtrans[nd.name]&&!nd.transform.isEqual(origtrans[nd.name])){
+ var lvec=nd.transform.transformDirection(new Vector3(1,0,0));
+ var uvec=nd.transform.transformDirection(new Vector3(0,1,0));
+ var vvec=nd.transform.transformDirection(new Vector3(0,0,1));
+ respart+=' TRANSFORM='
+ +lvec.x+' '+lvec.y+' '+lvec.z+' '
+ +uvec.x+' '+uvec.y+' '+uvec.z+' '
+ +vvec.x+' '+vvec.y+' '+vvec.z+' '
+ +nd.transform.translation.x+' '
+ +nd.transform.translation.y+' '
+ +nd.transform.translation.z+'\n';
+ defaultvals=false;
+ }
+ respart+=' END\n';
+ if(!defaultvals) res+=respart;
+ }
+ if(clip){
+ var centre=clip.transform.translation;
+ var normal=clip.transform.transformDirection(new Vector3(0,0,1));
+ res+=' CROSSSECT\n';
+ if(!(centre.x==0 && centre.y==0 && centre.z==0))
+ res+=host.util.printf(
+ ' CENTER=%s %s %s\n', centre.x, centre.y, centre.z);
+ if(!(normal.x==1 && normal.y==0 && normal.z==0))
+ res+=host.util.printf(
+ ' NORMAL=%s %s %s\n', normal.x, normal.y, normal.z);
+ res+=host.util.printf(
+ ' VISIBLE=%s\n', clip.visible);
+ res+=host.util.printf(
+ ' PLANECOLOR=%s %s %s\n', clip.material.emissiveColor.r,
+ clip.material.emissiveColor.g, clip.material.emissiveColor.b);
+ res+=host.util.printf(
+ ' OPACITY=%s\n', clip.opacity);
+ res+=host.util.printf(
+ ' INTERSECTIONCOLOR=%s %s %s\n',
+ clip.wireframeColor.r, clip.wireframeColor.g, clip.wireframeColor.b);
+ res+=' END\n';
+// for(var propt in clip){
+// console.println(propt+':'+clip[propt]);
+// }
+ }
+ res+='END\n';
+ host.console.show();
+// host.console.clear();
+ host.console.println('%%\n%% Add the following VIEW section to a file of\n'+
+ '%% predefined views (See option "3Dviews"!).\n%%\n' +
+ '%% The view may be given a name after VIEW=...\n' +
+ '%% (Remove \'%\' in front of \'=\'.)\n%%');
+ host.console.println(res + '\n');
+}
+
+//add items to 3D context menu
+runtime.addCustomMenuItem("dfltview", "Generate Default View", "default", 0);
+runtime.addCustomMenuItem("currview", "Get Current View", "default", 0);
+runtime.addCustomMenuItem("csection", "Cross Section", "checked", 0);
+
+//menu event handlers
+menuEventHandler = new MenuEventHandler();
+menuEventHandler.onEvent = function(e) {
+ switch(e.menuItemName){
+ case "dfltview": calc3Dopts(); break;
+ case "currview": get3Dview(); break;
+ case "csection":
+ addremoveClipPlane(e.menuItemChecked);
+ break;
+ }
+};
+runtime.addEventHandler(menuEventHandler);
+
+//global variable taking reference to currently selected node;
+var target=null;
+selectionEventHandler=new SelectionEventHandler();
+selectionEventHandler.onEvent=function(e){
+ if(e.selected&&e.node.name!=''){
+ target=e.node;
+ }else{
+ target=null;
+ }
+}
+runtime.addEventHandler(selectionEventHandler);
+
+cameraEventHandler=new CameraEventHandler();
+cameraEventHandler.onEvent=function(e){
+ var clip=null;
+ runtime.removeCustomMenuItem("csection");
+ runtime.addCustomMenuItem("csection", "Cross Section", "checked", 0);
+ if(clip=scene.nodes.getByName('$$$$$$')|| //predefined
+ scene.nodes.getByName('Clipping Plane')){ //added via context menu
+ runtime.removeCustomMenuItem("csection");
+ runtime.addCustomMenuItem("csection", "Cross Section", "checked", 1);
+ }
+ if(clip){//plane in predefined views must be rotated by 90 deg around normal
+ clip.transform.rotateAboutLineInPlace(
+ Math.PI/2,clip.transform.translation,
+ clip.transform.transformDirection(new Vector3(0,0,1))
+ );
+ }
+ for(var i=0; i<rot4x4.length; i++){rot4x4[i].setIdentity()}
+ target=null;
+}
+runtime.addEventHandler(cameraEventHandler);
+
+var rot4x4=new Array(); //keeps track of spin and tilt axes transformations
+//key event handler for scaling moving, spinning and tilting objects
+keyEventHandler=new KeyEventHandler();
+keyEventHandler.onEvent=function(e){
+ var backtrans=new Matrix4x4();
+ var trgt=null;
+ if(target) {
+ trgt=target;
+ var backtrans=new Matrix4x4();
+ var trans=trgt.transform;
+ var parent=trgt.parent;
+ while(parent.transform){
+ //build local to world transformation matrix
+ trans.multiplyInPlace(parent.transform);
+ //also build world to local back-transformation matrix
+ backtrans.multiplyInPlace(parent.transform.inverse.transpose);
+ parent=parent.parent;
+ }
+ backtrans.transposeInPlace();
+ }else{
+ if(
+ trgt=scene.nodes.getByName('$$$$$$')||
+ trgt=scene.nodes.getByName('Clipping Plane')
+ ) var trans=trgt.transform;
+ }
+ if(!trgt) return;
+
+ var tname=trgt.name;
+ if(typeof(rot4x4[tname])=='undefined') rot4x4[tname]=new Matrix4x4();
+ if(target)
+ var tiltAxis=rot4x4[tname].transformDirection(new Vector3(0,1,0));
+ else
+ var tiltAxis=trans.transformDirection(new Vector3(0,1,0));
+ var spinAxis=rot4x4[tname].transformDirection(new Vector3(0,0,1));
+
+ //get the centre of the mesh
+ if(target&&trgt.constructor.name=='Mesh'){
+ var centre=trans.transformPosition(trgt.computeBoundingBox().center);
+ }else{ //part group (Node3 parent node, clipping plane)
+ var centre=new Vector3(trans.translation);
+ }
+ switch(e.characterCode){
+ case 30://tilt up
+ rot4x4[tname].rotateAboutLineInPlace(
+ -Math.PI/900,rot4x4[tname].translation,tiltAxis);
+ trans.rotateAboutLineInPlace(-Math.PI/900,centre,tiltAxis);
+ break;
+ case 31://tilt down
+ rot4x4[tname].rotateAboutLineInPlace(
+ Math.PI/900,rot4x4[tname].translation,tiltAxis);
+ trans.rotateAboutLineInPlace(Math.PI/900,centre,tiltAxis);
+ break;
+ case 28://spin right
+ if(e.ctrlKeyDown&&target){
+ trans.rotateAboutLineInPlace(-Math.PI/900,centre,spinAxis);
+ }else{
+ rot4x4[tname].rotateAboutLineInPlace(
+ -Math.PI/900,rot4x4[tname].translation,new Vector3(0,0,1));
+ trans.rotateAboutLineInPlace(-Math.PI/900,centre,new Vector3(0,0,1));
+ }
+ break;
+ case 29://spin left
+ if(e.ctrlKeyDown&&target){
+ trans.rotateAboutLineInPlace(Math.PI/900,centre,spinAxis);
+ }else{
+ rot4x4[tname].rotateAboutLineInPlace(
+ Math.PI/900,rot4x4[tname].translation,new Vector3(0,0,1));
+ trans.rotateAboutLineInPlace(Math.PI/900,centre,new Vector3(0,0,1));
+ }
+ break;
+ case 120: //x
+ translateTarget(trans, new Vector3(1,0,0), e);
+ break;
+ case 121: //y
+ translateTarget(trans, new Vector3(0,1,0), e);
+ break;
+ case 122: //z
+ translateTarget(trans, new Vector3(0,0,1), e);
+ break;
+ case 88: //shift + x
+ translateTarget(trans, new Vector3(-1,0,0), e);
+ break;
+ case 89: //shift + y
+ translateTarget(trans, new Vector3(0,-1,0), e);
+ break;
+ case 90: //shift + z
+ translateTarget(trans, new Vector3(0,0,-1), e);
+ break;
+ case 115: //s
+ trans.translateInPlace(centre.scale(-1));
+ trans.scaleInPlace(1.01);
+ trans.translateInPlace(centre.scale(1));
+ break;
+ case 83: //shift + s
+ trans.translateInPlace(centre.scale(-1));
+ trans.scaleInPlace(1/1.01);
+ trans.translateInPlace(centre.scale(1));
+ break;
+ }
+ trans.multiplyInPlace(backtrans);
+}
+runtime.addEventHandler(keyEventHandler);
+
+//translates object by amount calculated from Canvas size
+function translateTarget(t, d, e){
+ var cam=scene.cameras.getByIndex(0);
+ if(cam.projectionType==cam.TYPE_PERSPECTIVE){
+ var scale=Math.tan(cam.fov/2)
+ *cam.targetPosition.subtract(cam.position).length
+ /Math.min(e.canvasPixelWidth,e.canvasPixelHeight);
+ }else{
+ var scale=cam.viewPlaneSize/2
+ /Math.min(e.canvasPixelWidth,e.canvasPixelHeight);
+ }
+ t.translateInPlace(d.scale(scale));
+}
+
+function addremoveClipPlane(chk) {
+ var curTrans=getCurTrans();
+ var clip=scene.createClippingPlane();
+ if(chk){
+ //add Clipping Plane and place its center either into the camera target
+ //position or into the centre of the currently selected mesh node
+ var centre=new Vector3();
+ if(target){
+ var trans=target.transform;
+ var parent=target.parent;
+ while(parent.transform){
+ trans=trans.multiply(parent.transform);
+ parent=parent.parent;
+ }
+ if(target.constructor.name=='Mesh'){
+ var centre=trans.transformPosition(target.computeBoundingBox().center);
+ }else{
+ var centre=new Vector3(trans.translation);
+ }
+ target=null;
+ }else{
+ centre.set(scene.cameras.getByIndex(0).targetPosition);
+ }
+ clip.transform.setView(
+ new Vector3(0,0,0), new Vector3(1,0,0), new Vector3(0,1,0));
+ clip.transform.translateInPlace(centre);
+ }else{
+ if(
+ scene.nodes.getByName('$$$$$$')||
+ scene.nodes.getByName('Clipping Plane')
+ ){
+ clip.remove();clip=null;
+ }
+ }
+ restoreTrans(curTrans);
+ return clip;
+}
+
+//function to store current transformation matrix of all nodes in the scene
+function getCurTrans() {
+ var tA=new Array();
+ for(var i=0; i<scene.nodes.count; i++){
+ var nd=scene.nodes.getByIndex(i);
+ if(nd.name=='') continue;
+ tA[nd.name]=new Matrix4x4(nd.transform);
+ }
+ return tA;
+}
+
+//function to restore transformation matrices given as arg
+function restoreTrans(tA) {
+ for(var i=0; i<scene.nodes.count; i++){
+ var nd=scene.nodes.getByIndex(i);
+ if(tA[nd.name]) nd.transform.set(tA[nd.name]);
+ }
+}
+
+//store original transformation matrix of all mesh nodes in the scene
+var origtrans=getCurTrans();
+
+//set initial state of "Cross Section" menu entry
+cameraEventHandler.onEvent(1);
+
+//host.console.clear();
diff --git a/macros/latex/contrib/media9/javascript/3Dspintool.js b/macros/latex/contrib/media9/javascript/3Dspintool.js
new file mode 100644
index 0000000000..529288ad0c
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/3Dspintool.js
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012, Alexander Grahn
+//
+// 3Dspintool.js
+//
+// version 20120301
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 3D JavaScript to be used with media9.sty (option `add3Djscript')
+//
+// enables the Spin tool (also accessible via 3D toolbar or context menu)
+// upon activation of the 3D scene; the scene then rotates around the upright
+// axis while dragging with the mouse
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+runtime.setCurrentTool(runtime.TOOL_NAME_SPIN);
diff --git a/macros/latex/contrib/media9/javascript/animation.js b/macros/latex/contrib/media9/javascript/animation.js
new file mode 100644
index 0000000000..4f202c4e7d
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/animation.js
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012, Alexander Grahn
+//
+// animation.js
+//
+// version 20120301
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// JavaScript for use with `add3DJScript' option of \includemedia
+//
+// * Activates keyframe animation embedded in the u3d file.
+// * Arrow keys `Down', `Up' can be used for speeding up and
+// slowing down a running animation, key `Home' for reverting
+// to the default speed.
+//
+// * Adjustable parameters:
+var rate = 1; // 1 --> use original speed as default
+var palindrome = true; // true --> play forth and back
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//get the first animation in the scene
+var myAnim = scene.animations.getByIndex(0);
+myAnim.wallTime = 0;
+myAnim.speed = 1;
+myAnim.myLength = myAnim.endTime - myAnim.startTime;
+scene.activateAnimation(myAnim);
+
+//method to set animation speed
+myAnim.setSpeed = function (speed) {
+ speed = Math.abs(speed);
+ this.wallTime /= speed/this.speed; //correct the walltime
+ this.speed = speed;
+};
+
+//method to change animation speed by a factor
+myAnim.changeSpeed = function (mult) {
+ this.wallTime /= mult; //correct the walltime
+ this.speed *= mult;
+};
+
+//set default speed
+myAnim.setSpeed(rate);
+
+//menu items
+runtime.addCustomMenuItem("faster", "Faster (Key Up)", "default", 0);
+runtime.addCustomMenuItem("slower", "Slower (Key Down)", "default", 0);
+runtime.addCustomMenuItem("default", "Default Speed (Key Home)", "default", 0);
+
+//menu handler to control speed
+menuEventHandler = new MenuEventHandler();
+menuEventHandler.onEvent = function(e) {
+ if (e.menuItemName == "faster") {
+ myAnim.changeSpeed(1.25);
+ }
+ else if (e.menuItemName == "slower") {
+ myAnim.changeSpeed(1/1.25);
+ }
+ else if (e.menuItemName == "default") {
+ myAnim.setSpeed(rate);
+ }
+};
+runtime.addEventHandler(menuEventHandler);
+
+//key handler to control speed
+keyEventHandler = new KeyEventHandler();
+keyEventHandler.onKeyDown = true;
+keyEventHandler.onEvent = function(e) {
+ switch(e.characterCode) {
+ case 30: //key up
+ myAnim.changeSpeed(1.05);
+ break;
+
+ case 31: //key down
+ myAnim.changeSpeed(1/1.05);
+ break;
+
+ case 4: //key home
+ myAnim.setSpeed(rate);
+ break;
+
+ case 1: //key end
+ myAnim.setSpeed(rate);
+ break;
+ }
+};
+runtime.addEventHandler(keyEventHandler);
+
+//run the animation using a TimeEventHandler
+myTimer = new TimeEventHandler();
+myTimer.onTimeChange = true;
+myTimer.onEvent = function(e) {
+ myAnim.wallTime += e.deltaTime;
+ if (palindrome == true) {
+ myAnim.currentTime =
+ myAnim.startTime
+ + myAnim.myLength/2
+ * (1 - Math.cos(Math.PI * myAnim.speed/myAnim.myLength * myAnim.wallTime));
+ } else {
+ myAnim.currentTime = myAnim.startTime
+ + (myAnim.speed * myAnim.wallTime % myAnim.myLength);
+ }
+};
+runtime.addEventHandler(myTimer);
diff --git a/macros/latex/contrib/media9/javascript/asylabels.js b/macros/latex/contrib/media9/javascript/asylabels.js
new file mode 100644
index 0000000000..95f1c3c3d1
--- /dev/null
+++ b/macros/latex/contrib/media9/javascript/asylabels.js
@@ -0,0 +1,85 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) 2012, Michail Vidiassov, John C. Bowman, Alexander Grahn
+//
+// asylabels.js
+//
+// version 20120912
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 3D JavaScript to be used with media9.sty (option `add3Djscript') for
+// Asymptote generated PRC files
+//
+// adds billboard behaviour to text labels in Asymptote PRC files so that
+// they always face the camera under 3D rotation.
+//
+//
+// This work may be distributed and/or modified under the
+// conditions of the LaTeX Project Public License.
+//
+// The latest version of this license is in
+// http://mirrors.ctan.org/macros/latex/base/lppl.txt
+//
+// This work has the LPPL maintenance status `maintained'.
+//
+// The Current Maintainer of this work is A. Grahn.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+var bbnodes=new Array(); // billboard meshes
+var bbtrans=new Array(); // billboard transforms
+
+function fulltransform(mesh)
+{
+ var t=new Matrix4x4(mesh.transform);
+ if(mesh.parent.name != "") {
+ var parentTransform=fulltransform(mesh.parent);
+ t.multiplyInPlace(parentTransform);
+ return t;
+ } else
+ return t;
+}
+
+// find all text labels in the scene and determine pivoting points
+var nodes=scene.nodes;
+var nodescount=nodes.count;
+var third=1.0/3.0;
+for(var i=0; i < nodescount; i++) {
+ var node=nodes.getByIndex(i);
+ var name=node.name;
+ var end=name.lastIndexOf(".")-1;
+ if(end > 0) {
+ if(name.charAt(end) == "\001") {
+ var start=name.lastIndexOf("-")+1;
+ if(end > start) {
+ node.name=name.substr(0,start-1);
+ var nodeMatrix=fulltransform(node.parent);
+ var c=nodeMatrix.translation; // position
+ var d=Math.pow(Math.abs(nodeMatrix.determinant),third); // scale
+ bbnodes.push(node);
+ bbtrans.push(Matrix4x4().scale(d,d,d).translate(c).multiply(nodeMatrix.inverse));
+ }
+ }
+ }
+}
+
+var camera=scene.cameras.getByIndex(0);
+var zero=new Vector3(0,0,0);
+var bbcount=bbnodes.length;
+
+// event handler to maintain camera-facing text labels
+billboardHandler=new RenderEventHandler();
+billboardHandler.onEvent=function(event)
+{
+ var T=new Matrix4x4();
+ T.setView(zero,camera.position.subtract(camera.targetPosition),
+ camera.up.subtract(camera.position));
+
+ for(var j=0; j < bbcount; j++)
+ bbnodes[j].transform.set(T.multiply(bbtrans[j]));
+ runtime.refresh();
+}
+runtime.addEventHandler(billboardHandler);
+
+runtime.refresh();