//////////////////////////////////////////////////////////////////////////////// // // (C) 2012, Alexander Grahn // // 3Dmenu.js // // version 20120912 // //////////////////////////////////////////////////////////////////////////////// // // 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 and be tilted against and spun // around the upright Z axis using the Up/Down and Left/Right arrow keys. // // 2.) Enables manipulation of position and orientation of indiviual parts in // the 3D scene. Parts which have been selected with the mouse can be // moved around and rotated like the cross section as described above, as // well as scaled using the s and S keys. // // This work may be distributed and/or modified under the // conditions of the LaTeX Project Public License, either version 1.3 // of this license or (at your option) any later version. // The latest version of this license is in // http://www.latex-project.org/lppl.txt // and version 1.3 or later is part of all distributions of LaTeX // version 2005/12/01 or later. // // 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 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 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'; for(var i=0;i0) var meshUserName=mesh.name.substr(0,end); else var meshUserName=mesh.name; respart=' PART='+meshUserName+'\n'; respart+=' UTF16NAME='+meshUTFName+'\n'; defaultvals=true; if(!mesh.visible){ respart+=' VISIBLE=false\n'; defaultvals=false; } if(mesh.opacity<1.0){ respart+=' OPACITY='+mesh.opacity+'\n'; defaultvals=false; } currender=defaultrender; switch(mesh.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(!mesh.transform.isEqual(origtrans[mesh.name])){ var lvec=mesh.transform.transformDirection(new Vector3(1,0,0)); var uvec=mesh.transform.transformDirection(new Vector3(0,1,0)); var vvec=mesh.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+' ' +mesh.transform.translation.x+' ' +mesh.transform.translation.y+' ' +mesh.transform.translation.z+'\n'; defaultvals=false; } respart+=' END\n'; if(!defaultvals) res+=respart; } //detect existing Clipping Plane (3D Cross Section) var clip=null; for(i=0; i