/* $RCSfile$
* $Author: hansonr $
* $Date: 2007-05-18 15:41:42 -0500 (Fri, 18 May 2007) $
* $Revision: 7752 $
*
* Copyright (C) 2003-2005 The Jmol Development Team
*
* Contact: jmol-developers@lists.sf.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jmol.export;
import java.util.BitSet;
import java.util.Hashtable;
import java.util.List;
import java.util.ArrayList;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Point3f;
import javax.vecmath.Tuple3f;
import javax.vecmath.Vector3f;
import org.jmol.g3d.Font3D;
import org.jmol.g3d.Graphics3D;
import org.jmol.util.Escape;
import org.jmol.util.Quaternion;
import org.jmol.viewer.Viewer;
public class _VrmlExporter extends __CartesianExporter {
/*
* 1/2009 Angel Herraez: # added support for translucency # Jmol info in
* header # set navigation mode # added support for background color # added
* support for labels: text, font face and font style; size is hardcoded to
* 0.4A
*/
public _VrmlExporter() {
useTable = new UseTable("USE ");
commentChar = "# ";
}
protected void output(Tuple3f pt) {
output(round(pt));
}
protected UseTable useTable;
protected void outputHeader() {
output("#VRML V2.0 utf8 Generated by Jmol " + Viewer.getJmolVersion()
+ "\n");
output("WorldInfo { \n");
output(" title " + Escape.escape(viewer.getModelSetName()) + "\n");
output(" info [ \"Generated by Jmol " + Viewer.getJmolVersion() + " \", \n");
output(" \"http://www.jmol.org \", \n");
output(" \"Creation date: " + getExportDate() + " \" ]\n");
output("} \n");
output("NavigationInfo { type \"EXAMINE\" } \n");
// puts the viewer into model-rotation mode
output("Background { skyColor ["
+ rgbFractionalFromColix(backgroundColix, ' ') + "] } \n");
// next is an approximation only
float angle = (float) (aperatureAngle * Math.PI / 180);
viewer.getAxisAngle(viewpoint);
output("Viewpoint{fieldOfView " + angle
+ " position " + cameraPosition.x + " " + cameraPosition.y + " " + cameraPosition.z
+ " orientation " + viewpoint.x + " " + viewpoint.y + " " + (viewpoint.angle == 0 ? 1 : viewpoint.z) + " " + -viewpoint.angle);
output("\n jump TRUE description \"v1\"\n}\n\n");
output(getJmolPerspective());
output("\nTransform{children Transform{translation ");
tempP1.set(center);
tempP1.scale(-1);
output(tempP1);
output("\nchildren [\n");
}
protected void outputFooter() {
useTable = null;
output("\n]\n");
output("}}\n");
}
protected void outputAppearance(short colix, boolean isText) {
String def = useTable.getDef((isText ? "T" : "") + colix);
output(" appearance ");
if (def.charAt(0) == '_') {
String color = rgbFractionalFromColix(colix, ' ');
output(" DEF " + def + " Appearance{material Material{diffuseColor ");
if (isText)
output(" 0 0 0 specularColor 0 0 0 ambientIntensity 0.0 shininess 0.0 emissiveColor "
+ color + " }}");
else
output(color + " transparency " + translucencyFractionalFromColix(colix) + "}}");
return;
}
output(def);
}
protected void outputCircle(Point3f pt1, Point3f pt2, float radius, short colix,
boolean doFill) {
if (doFill) {
// draw filled circle
output("Transform{translation ");
tempV1.set(pt1);
tempV1.add(pt2);
tempV1.scale(0.5f);
output(tempV1);
output(" children Billboard{axisOfRotation 0 0 0 children Transform{rotation 1 0 0 1.5708");
outputCylinderChild(pt1, pt2, colix, Graphics3D.ENDCAPS_FLAT,
(int) (radius * 2000));
output("}}}\n");
return;
}
// draw a thin torus
String child = useTable.getDef("C" + colix + "_" + radius);
outputTransRot(pt1, pt2, 0, 0, 1);
tempP3.set(1, 1, 1);
tempP3.scale(radius);
output(" scale ");
output(tempP3);
output(" children ");
if (child.charAt(0) == '_') {
output("DEF " + child);
output(" Billboard{axisOfRotation 0 0 0 children Transform{children");
output(" Shape{geometry Extrusion{beginCap FALSE convex FALSE endCap FALSE creaseAngle 1.57");
output(" crossSection [");
float rpd = 3.1415926f / 180;
float scale = 0.02f / radius;
for (int i = 0; i <= 360; i += 10) {
output(round(Math.cos(i * rpd) * scale) + " ");
output(round(Math.sin(i * rpd) * scale) + " ");
}
output("] spine [");
for (int i = 0; i <= 360; i += 10) {
output(round(Math.cos(i * rpd)) + " ");
output(round(Math.sin(i * rpd)) + " 0 ");
}
output("]}");
outputAppearance(colix, false);
output("}}}");
} else {
output(child);
}
output("}\n");
}
protected void outputCone(Point3f ptBase, Point3f ptTip, float radius,
short colix) {
float height = tempP1.distance(tempP2);
outputTransRot(tempP1, tempP2, 0, 1, 0);
output(" children ");
String cone = "o" + (int) (height * 100) + "_" + (int) (radius * 100);
String child = useTable.getDef("c" + cone + "_" + colix);
if (child.charAt(0) == '_') {
output("DEF " + child + " Shape{geometry ");
cone = useTable.getDef(cone);
if (cone.charAt(0) == '_') {
output("DEF " + cone + " Cone{height " + round(height)
+ " bottomRadius " + round(radius) + "}");
} else {
output(cone);
}
outputAppearance(colix, false);
output("}");
} else {
output(child);
}
output("}\n");
}
protected boolean outputCylinder(Point3f ptCenter, Point3f pt1, Point3f pt2,
short colix, byte endcaps, float radius, Point3f ptX, Point3f ptY) {
if (ptX == null) {
outputTransRot(pt1, pt2, 0, 1, 0);
} else {
output("Transform{translation ");
output(ptCenter);
outputQuaternionFrame(ptCenter, ptY, pt1, ptX, 2, " ", "");
pt1.set(0, 0, -1);
pt2.set(0, 0, 1);
}
outputCylinderChild(pt1, pt2, colix, endcaps, radius);
output("}\n");
if (endcaps == Graphics3D.ENDCAPS_SPHERICAL) {
outputSphere(pt1, radius*1.01f, colix);
outputSphere(pt2, radius*1.01f, colix);
}
return true;
}
private void outputCylinderChild(Point3f pt1, Point3f pt2, short colix,
byte endcaps, float radius) {
output(" children ");
float length = pt1.distance(pt2);
String child = useTable.getDef("C" + colix + "_" + (int) (length * 100) + "_" + radius
+ "_" + endcaps);
if (child.charAt(0) == '_') {
output("DEF " + child);
output(" Shape{geometry ");
String cyl = useTable.getDef("c" + round(length) + "_" + endcaps + "_" + radius);
if (cyl.charAt(0) == '_') {
output("DEF " + cyl + " Cylinder{height "
+ round(length) + " radius " + radius
+ (endcaps == Graphics3D.ENDCAPS_FLAT ? "" : " top FALSE bottom FALSE") + "}");
} else {
output(cyl);
}
outputAppearance(colix, false);
output("}");
} else {
output(child);
}
}
protected void outputEllipsoid(Point3f ptCenter, Point3f[] points, short colix) {
output("Transform{translation ");
output(ptCenter);
// Hey, hey -- quaternions to the rescue!
// Just send three points to Quaternion to define a plane and return
// the AxisAngle required to rotate to that position. That's all there is to
// it.
outputQuaternionFrame(ptCenter, points[1], points[3], points[5], 1, " ", "");
output(" children ");
tempP3.set(0, 0, 0);
outputSphereChild(tempP3, 1.0f, colix);
output("}\n");
}
private Point3f tempQ1 = new Point3f();
private Point3f tempQ2 = new Point3f();
protected void outputQuaternionFrame(Point3f ptCenter, Point3f ptX,
Point3f ptY, Point3f ptZ, float yScale,
String pre, String post) {
//Hey, hey -- quaternions to the rescue!
// Just send three points to Quaternion to define a plane and return
// the AxisAngle required to rotate to that position. That's all there is to it.
tempQ1.set(ptX);
tempQ2.set(ptY);
AxisAngle4f a = Quaternion.getQuaternionFrame(ptCenter, tempQ1, tempQ2)
.toAxisAngle4f();
if (!Float.isNaN(a.x)) {
output(" rotation");
output(pre);
output(a.x + " " + a.y + " " + a.z + " " + a.angle);
output(post);
}
float sx = ptX.distance(ptCenter);
float sy = ptY.distance(ptCenter) * yScale;
float sz = ptZ.distance(ptCenter);
output(" scale");
output(pre);
output(sx + " " + sy + " " + sz);
output(post);
}
protected void outputSurface(Point3f[] vertices, Vector3f[] normals,
short[] colixes, int[][] indices,
short[] polygonColixes,
int nVertices, int nPolygons, int nFaces, BitSet bsFaces,
int faceVertexMax, short colix, List colorList, Hashtable htColixes, Point3f offset) {
output("Shape {\n");
outputAppearance(colix, false);
output(" geometry IndexedFaceSet {\n");
if (polygonColixes != null)
output(" colorPerVertex FALSE\n");
// coordinates
output("coord Coordinate {\n point [\n");
outputVertices(vertices, nVertices, offset);
output(" ]\n");
output(" }\n");
output(" coordIndex [\n");
int[] map = new int[nVertices];
getCoordinateMap(vertices, map);
outputIndices(indices, map, nPolygons, bsFaces, faceVertexMax);
output(" ]\n");
// normals
if (normals != null) {
List vNormals = new ArrayList();
map = getNormalMap(normals, nVertices, vNormals);
output(" solid FALSE\n normalPerVertex TRUE\n normal Normal {\n vector [\n");
outputNormals(vNormals);
output(" ]\n");
output(" }\n");
output(" normalIndex [\n");
outputIndices(indices, map, nPolygons, bsFaces, faceVertexMax);
output(" ]\n");
}
map = null;
// colors
if (colorList != null) {
output(" color Color { color [\n");
outputColors(colorList);
output(" ] } \n");
output(" colorIndex [\n");
outputColorIndices(indices, nPolygons, bsFaces, faceVertexMax, htColixes, colixes, polygonColixes);
output(" ]\n");
}
output(" }\n");
output("}\n");
}
protected void outputFace(int[] face, int[] map, int faceVertexMax) {
output(map[face[0]] + " " + map[face[1]] + " " + map[face[2]] + " -1\n");
if (faceVertexMax == 4 && face.length == 4)
output(map[face[0]] + " " + map[face[2]] + " " + map[face[3]] + " -1\n");
}
protected void outputNormals(List vNormals) {
int n = vNormals.size();
for (int i = 0; i < n; i++)
output((String) vNormals.get(i));
}
protected void outputColors(List colorList) {
int nColors = colorList.size();
for (int i = 0; i < nColors; i++) {
String color = rgbFractionalFromColix(((Short) colorList.get(i)).shortValue(),
' ');
output(" ");
output(color);
output("\n");
}
}
protected void outputColorIndices(int[][] indices, int nPolygons, BitSet bsFaces,
int faceVertexMax, Hashtable htColixes,
short[] colixes, short[] polygonColixes) {
boolean isAll = (bsFaces == null);
int i0 = (isAll ? nPolygons - 1 : bsFaces.nextSetBit(0));
for (int i = i0; i >= 0; i = (isAll ? i - 1 : bsFaces.nextSetBit(i + 1))) {
if (polygonColixes == null) {
output(htColixes.get("" + colixes[indices[i][0]]) + " "
+ htColixes.get("" + colixes[indices[i][1]]) + " "
+ htColixes.get("" + colixes[indices[i][2]]) + " -1\n");
if (faceVertexMax == 4 && indices[i].length == 4)
output(htColixes.get("" + colixes[indices[i][0]]) + " "
+ htColixes.get("" + colixes[indices[i][2]]) + " "
+ htColixes.get("" + colixes[indices[i][3]]) + " -1\n");
} else {
output(htColixes.get("" + polygonColixes[i]) + "\n");
}
}
}
private Hashtable htSpheresRendered = new Hashtable();
protected void outputSphere(Point3f ptCenter, float radius, short colix) {
int iRad = (int) (radius * 100);
String check = round(ptCenter) + " " + iRad;
if (htSpheresRendered.get(check) != null)
return;
htSpheresRendered.put(check, Boolean.TRUE);
outputSphereChild(ptCenter, radius, colix);
}
protected void outputSphereChild(Point3f ptCenter, float radius, short colix) {
int iRad = (int) (radius * 100);
String child = useTable.getDef("S" + colix + "_" + iRad);
output("Transform{translation ");
output(ptCenter);
output(" children ");
if (child.charAt(0) == '_') {
output("DEF " + child);
output(" Shape{geometry Sphere{radius " + radius + "}");
outputAppearance(colix, false);
output("}");
} else {
output(child);
}
output("}\n");
}
protected void outputTextPixel(Point3f pt, int argb) {
String color = rgbFractionalFromArgb(argb, ' ');
output("Transform{translation ");
output(pt);
output(" children ");
String child = useTable.getDef("p" + argb);
if (child.charAt(0) == '_') {
output("DEF " + child + " Shape{geometry Sphere{radius 0.01}");
output(" appearance Appearance{material Material{diffuseColor 0 0 0 specularColor 0 0 0 ambientIntensity 0.0 shininess 0.0 emissiveColor "
+ color + " }}}");
} else {
output(child);
}
output("}\n");
}
private void outputTransRot(Point3f pt1, Point3f pt2, int x, int y, int z) {
output("Transform{");
outputTransRot(pt1, pt2, x, y, z, " ", "");
}
protected void outputTransRot(Point3f pt1, Point3f pt2, int x, int y, int z,
String pre, String post) {
tempV1.set(pt2);
tempV1.add(pt1);
tempV1.scale(0.5f);
output("translation");
output(pre);
output(tempV1);
output(post);
tempV1.sub(pt1);
tempV1.normalize();
tempV2.set(x, y, z);
tempV2.add(tempV1);
tempA.set(tempV2.x, tempV2.y, tempV2.z, 3.14159f);
output(" rotation");
output(pre);
output(round(tempA.x) + " " + round(tempA.y) + " " + round(tempA.z) + " "
+ round(tempA.angle));
output(post);
}
protected void outputTriangle(Point3f pt1, Point3f pt2, Point3f pt3, short colix) {
// nucleic base
// cartoons
output("Shape{geometry IndexedFaceSet{solid FALSE coord Coordinate{point[");
output(pt1);
output(" ");
output(pt2);
output(" ");
output(pt3);
output("]}coordIndex[ 0 1 2 -1 ]}");
outputAppearance(colix, false);
output("}\n");
}
void plotText(int x, int y, int z, short colix, String text, Font3D font3d) {
if (z < 3)
z = viewer.getFrontPlane();
String useFontStyle = font3d.fontStyle.toUpperCase();
String preFontFace = font3d.fontFace.toUpperCase();
String useFontFace = (preFontFace.equals("MONOSPACED") ? "TYPEWRITER"
: preFontFace.equals("SERIF") ? "SERIF" : "SANS");
output("Transform{translation ");
tempP3.set(x, y, z);
viewer.unTransformPoint(tempP3, tempP1);
output(tempP1);
// These x y z are 3D coordinates of echo or the atom the label is attached
// to.
output(" children ");
String child = useTable.getDef("T" + colix + useFontFace + useFontStyle + "_" + text);
if (child.charAt(0) == '_') {
output("DEF " + child + " Billboard{axisOfRotation 0 0 0 children Transform{children Shape{");
outputAppearance(colix, true);
output(" geometry Text{fontStyle ");
String fontstyle = useTable.getDef("F" + useFontFace + useFontStyle);
if (fontstyle.charAt(0) == '_') {
output("DEF " + fontstyle + " FontStyle{size 0.4 family \"" + useFontFace
+ "\" style \"" + useFontStyle + "\"}");
} else {
output(fontstyle);
}
output(" string " + Escape.escape(text) + "}}}}");
} else {
output(child);
}
output("}\n");
}
/*
* Unsolved issues: # Non-label texts: echos, measurements :: need to get
* space coordinates, not screen coord. # Font size: not implemented; 0.4A
* is hardcoded (resizes with zoom) Java VRML font3d.fontSize = 13.0 size
* (numeric), but in angstroms, not pixels font3d.fontSizeNominal = 13.0 #
* Label offsets: not implemented; hardcoded to 0.25A in each x,y,z #
* Multi-line labels: only the first line is received # Sub/superscripts not
* interpreted
*/
}