XYZApp.java revision 0
1008N/A/*
1008N/A * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
1008N/A *
1008N/A * Redistribution and use in source and binary forms, with or without
1008N/A * modification, are permitted provided that the following conditions
1008N/A * are met:
1008N/A *
1008N/A * - Redistributions of source code must retain the above copyright
1008N/A * notice, this list of conditions and the following disclaimer.
1008N/A *
1008N/A * - Redistributions in binary form must reproduce the above copyright
1008N/A * notice, this list of conditions and the following disclaimer in the
1008N/A * documentation and/or other materials provided with the distribution.
1008N/A *
1008N/A * - Neither the name of Sun Microsystems nor the names of its
1008N/A * contributors may be used to endorse or promote products derived
1008N/A * from this software without specific prior written permission.
1008N/A *
1008N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
1008N/A * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
1008N/A * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1008N/A * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
1008N/A * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
1008N/A * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
4129N/A * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1008N/A * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
1008N/A * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
1008N/A * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1008N/A * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1008N/A */
1008N/A
1008N/A/*
1008N/A */
1008N/A
1008N/A/*
1008N/A * A set of classes to parse, represent and display Chemical compounds in
1008N/A * .xyz format (see http://chem.leeds.ac.uk/Project/MIME.html)
1008N/A */
1008N/A
1008N/Aimport java.applet.Applet;
1008N/Aimport java.awt.Image;
1008N/Aimport java.awt.Event;
1008N/Aimport java.awt.Graphics;
1008N/Aimport java.awt.Dimension;
1008N/Aimport java.io.*;
1008N/Aimport java.net.URL;
1008N/Aimport java.util.Hashtable;
1008N/Aimport java.awt.image.IndexColorModel;
1008N/Aimport java.awt.image.ColorModel;
1008N/Aimport java.awt.image.MemoryImageSource;
1008N/Aimport java.awt.event.*;
1008N/A
1008N/A/** The representation of a Chemical .xyz model */
1008N/Aclass XYZChemModel {
1008N/A float vert[];
1008N/A Atom atoms[];
1008N/A int tvert[];
1008N/A int ZsortMap[];
1008N/A int nvert, maxvert;
1008N/A
1008N/A static Hashtable atomTable = new Hashtable();
1008N/A static Atom defaultAtom;
1008N/A static {
1008N/A atomTable.put("c", new Atom(0, 0, 0));
1008N/A atomTable.put("h", new Atom(210, 210, 210));
1008N/A atomTable.put("n", new Atom(0, 0, 255));
1008N/A atomTable.put("o", new Atom(255, 0, 0));
1008N/A atomTable.put("p", new Atom(255, 0, 255));
1008N/A atomTable.put("s", new Atom(255, 255, 0));
1008N/A atomTable.put("hn", new Atom(150, 255, 150)); /* !!*/
1008N/A defaultAtom = new Atom(255, 100, 200);
1008N/A }
1008N/A
1008N/A boolean transformed;
1008N/A Matrix3D mat;
1008N/A
1008N/A float xmin, xmax, ymin, ymax, zmin, zmax;
1008N/A
1008N/A
1008N/A XYZChemModel () {
1008N/A mat = new Matrix3D();
1008N/A mat.xrot(20);
1008N/A mat.yrot(30);
1008N/A }
1008N/A
1008N/A
2345N/A /** Create a Cehmical model by parsing an input stream */
1008N/A XYZChemModel (InputStream is) throws Exception
2517N/A {
2517N/A this();
2345N/A StreamTokenizer st = new StreamTokenizer(
2345N/A new BufferedReader(new InputStreamReader(is, "UTF-8")));
2345N/A st.eolIsSignificant(true);
2345N/A st.commentChar('#');
2345N/A int slot = 0;
1008N/A
1008N/A try
1548N/A {
1548N/Ascan:
1548N/A while (true)
1548N/A {
1548N/A switch ( st.nextToken() )
1548N/A {
1565N/A case StreamTokenizer.TT_EOF:
1565N/A break scan;
1548N/A default:
1548N/A break;
1565N/A case StreamTokenizer.TT_WORD:
1565N/A String name = st.sval;
1548N/A double x = 0, y = 0, z = 0;
1565N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER)
1548N/A {
1548N/A x = st.nval;
1008N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER)
1008N/A {
1008N/A y = st.nval;
1008N/A if (st.nextToken() == StreamTokenizer.TT_NUMBER)
1008N/A z = st.nval;
1008N/A }
1008N/A }
1008N/A addVert(name, (float) x, (float) y, (float) z);
1008N/A while( st.ttype != StreamTokenizer.TT_EOL &&
1008N/A st.ttype != StreamTokenizer.TT_EOF )
1008N/A st.nextToken();
1008N/A
1008N/A } // end Switch
1008N/A
1008N/A } // end while
1008N/A
1008N/A is.close();
1008N/A
1008N/A } // end Try
1008N/A catch( IOException e) {}
1008N/A
1008N/A if (st.ttype != StreamTokenizer.TT_EOF)
1008N/A throw new Exception(st.toString());
1008N/A
1008N/A } // end XYZChemModel()
1008N/A
1008N/A /** Add a vertex to this model */
1008N/A int addVert(String name, float x, float y, float z) {
1008N/A int i = nvert;
1008N/A if (i >= maxvert)
1008N/A if (vert == null) {
1008N/A maxvert = 100;
1008N/A vert = new float[maxvert * 3];
1008N/A atoms = new Atom[maxvert];
1008N/A } else {
1008N/A maxvert *= 2;
1008N/A float nv[] = new float[maxvert * 3];
1008N/A System.arraycopy(vert, 0, nv, 0, vert.length);
1008N/A vert = nv;
1008N/A Atom na[] = new Atom[maxvert];
1008N/A System.arraycopy(atoms, 0, na, 0, atoms.length);
1008N/A atoms = na;
2872N/A }
2872N/A Atom a = (Atom) atomTable.get(name.toLowerCase());
2872N/A if (a == null) a = defaultAtom;
2872N/A atoms[i] = a;
2872N/A i *= 3;
2872N/A vert[i] = x;
2872N/A vert[i + 1] = y;
2872N/A vert[i + 2] = z;
2872N/A return nvert++;
2872N/A }
2872N/A
2872N/A /** Transform all the points in this model */
2872N/A void transform() {
2872N/A if (transformed || nvert <= 0)
2872N/A return;
2872N/A if (tvert == null || tvert.length < nvert * 3)
2872N/A tvert = new int[nvert * 3];
2872N/A mat.transform(vert, tvert, nvert);
2872N/A transformed = true;
2872N/A }
1548N/A
1548N/A
1548N/A /** Paint this model to a graphics context. It uses the matrix associated
1548N/A with this model to map from model space to screen space.
1548N/A The next version of the browser should have double buffering,
1548N/A which will make this *much* nicer */
1565N/A void paint(Graphics g) {
1565N/A if (vert == null || nvert <= 0)
1565N/A return;
1548N/A transform();
1548N/A int v[] = tvert;
1565N/A int zs[] = ZsortMap;
1565N/A if (zs == null) {
1548N/A ZsortMap = zs = new int[nvert];
1565N/A for (int i = nvert; --i >= 0;)
1548N/A zs[i] = i * 3;
1548N/A }
2648N/A
2648N/A /*
2648N/A * I use a bubble sort since from one iteration to the next, the sort
2648N/A * order is pretty stable, so I just use what I had last time as a
2648N/A * "guess" of the sorted order. With luck, this reduces O(N log N)
2648N/A * to O(N)
2648N/A */
2648N/A
2648N/A for (int i = nvert - 1; --i >= 0;) {
2648N/A boolean flipped = false;
2648N/A for (int j = 0; j <= i; j++) {
2648N/A int a = zs[j];
2648N/A int b = zs[j + 1];
2648N/A if (v[a + 2] > v[b + 2]) {
2648N/A zs[j + 1] = a;
2648N/A zs[j] = b;
2648N/A flipped = true;
2648N/A }
2648N/A }
1008N/A if (!flipped)
1008N/A break;
1008N/A }
1008N/A
1008N/A int lg = 0;
1008N/A int lim = nvert;
1008N/A Atom ls[] = atoms;
1008N/A if (lim <= 0 || nvert <= 0)
1008N/A return;
1008N/A for (int i = 0; i < lim; i++) {
1008N/A int j = zs[i];
1008N/A int grey = v[j + 2];
1008N/A if (grey < 0)
1008N/A grey = 0;
1008N/A if (grey > 15)
1008N/A grey = 15;
1008N/A // g.drawString(names[i], v[j], v[j+1]);
1008N/A atoms[j/3].paint(g, v[j], v[j + 1], grey);
1008N/A // g.drawImage(iBall, v[j] - (iBall.width >> 1), v[j + 1] -
1008N/A // (iBall.height >> 1));
1008N/A }
1008N/A }
1008N/A
1008N/A /** Find the bounding box of this model */
1008N/A void findBB() {
1008N/A if (nvert <= 0)
1008N/A return;
1008N/A float v[] = vert;
1008N/A float xmin = v[0], xmax = xmin;
1008N/A float ymin = v[1], ymax = ymin;
1008N/A float zmin = v[2], zmax = zmin;
1008N/A for (int i = nvert * 3; (i -= 3) > 0;) {
1008N/A float x = v[i];
1008N/A if (x < xmin)
1008N/A xmin = x;
1008N/A if (x > xmax)
1008N/A xmax = x;
1008N/A float y = v[i + 1];
1008N/A if (y < ymin)
1008N/A ymin = y;
1008N/A if (y > ymax)
1008N/A ymax = y;
1008N/A float z = v[i + 2];
1008N/A if (z < zmin)
1008N/A zmin = z;
1008N/A if (z > zmax)
1008N/A zmax = z;
1008N/A }
1008N/A this.xmax = xmax;
1008N/A this.xmin = xmin;
1008N/A this.ymax = ymax;
1008N/A this.ymin = ymin;
2521N/A this.zmax = zmax;
1008N/A this.zmin = zmin;
1008N/A }
2345N/A}
1008N/A
1008N/A/** An applet to put a Chemical model into a page */
1008N/Apublic class XYZApp
1008N/A extends Applet
1008N/A implements Runnable, MouseListener, MouseMotionListener {
1008N/A XYZChemModel md;
1008N/A boolean painted = true;
1008N/A float xfac;
1008N/A int prevx, prevy;
1008N/A float xtheta, ytheta;
1008N/A float scalefudge = 1;
1008N/A Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();
1008N/A String mdname = null;
1008N/A String message = null;
1008N/A Image backBuffer;
1008N/A Graphics backGC;
1008N/A Dimension backSize;
1008N/A
1008N/A
1008N/A private synchronized void newBackBuffer() {
1008N/A backBuffer = createImage(getSize().width, getSize().height);
1008N/A if (backGC != null) {
1008N/A backGC.dispose();
1008N/A }
1008N/A backGC = backBuffer.getGraphics();
1008N/A backSize = getSize();
1008N/A }
1008N/A
1008N/A public void init() {
1008N/A mdname = getParameter("model");
1008N/A try {
1008N/A scalefudge = Float.valueOf(getParameter("scale")).floatValue();
1008N/A } catch(Exception e) {
1008N/A };
1008N/A amat.yrot(20);
1008N/A amat.xrot(20);
1008N/A if (mdname == null)
1008N/A mdname = "model.obj";
1008N/A resize(getSize().width <= 20 ? 400 : getSize().width,
1008N/A getSize().height <= 20 ? 400 : getSize().height);
1008N/A newBackBuffer();
1008N/A addMouseListener(this);
1008N/A addMouseMotionListener(this);
1008N/A }
1008N/A
1008N/A public void destroy() {
1008N/A removeMouseListener(this);
1008N/A removeMouseMotionListener(this);
1008N/A }
1008N/A
1008N/A public void run() {
1008N/A InputStream is = null;
1008N/A try {
1008N/A Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
1008N/A is = new URL(getDocumentBase(), mdname).openStream();
1008N/A XYZChemModel m = new XYZChemModel (is);
1008N/A Atom.setApplet(this);
1008N/A md = m;
1008N/A m.findBB();
1008N/A float xw = m.xmax - m.xmin;
1008N/A float yw = m.ymax - m.ymin;
1142N/A float zw = m.zmax - m.zmin;
1008N/A if (yw > xw)
1008N/A xw = yw;
1008N/A if (zw > xw)
1008N/A xw = zw;
1008N/A float f1 = getSize().width / xw;
1008N/A float f2 = getSize().height / xw;
1008N/A xfac = 0.7f * (f1 < f2 ? f1 : f2) * scalefudge;
1008N/A } catch(Exception e) {
1008N/A e.printStackTrace();
1142N/A md = null;
1008N/A message = e.toString();
1008N/A }
1008N/A try {
1008N/A if (is != null)
1008N/A is.close();
1008N/A } catch(Exception e) {
1008N/A }
1008N/A repaint();
1008N/A }
1008N/A public void start() {
1008N/A if (md == null && message == null)
1008N/A new Thread(this).start();
1008N/A }
1008N/A public void stop() {
1008N/A }
1008N/A /* event handling */
1008N/A public void mouseClicked(MouseEvent e) {
1008N/A }
1008N/A public void mousePressed(MouseEvent e) {
1008N/A prevx = e.getX();
1008N/A prevy = e.getY();
1008N/A e.consume();
1008N/A }
1008N/A public void mouseReleased(MouseEvent e) {
1008N/A }
1008N/A public void mouseEntered(MouseEvent e) {
1008N/A }
1008N/A public void mouseExited(MouseEvent e) {
1008N/A }
1008N/A public void mouseDragged(MouseEvent e) {
1008N/A int x = e.getX();
1008N/A int y = e.getY();
1008N/A tmat.unit();
1008N/A float xtheta = (prevy - y) * (360.0f / getSize().width);
1008N/A float ytheta = (x - prevx) * (360.0f / getSize().height);
1008N/A tmat.xrot(xtheta);
1008N/A tmat.yrot(ytheta);
1008N/A amat.mult(tmat);
1008N/A if (painted) {
1008N/A painted = false;
1008N/A repaint();
1008N/A }
1008N/A prevx = x;
1008N/A prevy = y;
1008N/A e.consume();
1008N/A }
1008N/A public void mouseMoved(MouseEvent e) {
1008N/A }
1008N/A
1008N/A public void update(Graphics g) {
1008N/A if (backBuffer == null)
1008N/A g.clearRect(0, 0, getSize().width, getSize().height);
1008N/A paint(g);
1008N/A }
1008N/A
1008N/A public void paint(Graphics g) {
1008N/A if (md != null) {
1008N/A md.mat.unit();
1008N/A md.mat.translate(-(md.xmin + md.xmax) / 2,
1008N/A -(md.ymin + md.ymax) / 2,
1008N/A -(md.zmin + md.zmax) / 2);
1008N/A md.mat.mult(amat);
1089N/A // md.mat.scale(xfac, -xfac, 8 * xfac / getSize().width);
1008N/A md.mat.scale(xfac, -xfac, 16 * xfac / getSize().width);
1008N/A md.mat.translate(getSize().width / 2, getSize().height / 2, 8);
1008N/A md.transformed = false;
1008N/A if (backBuffer != null) {
1008N/A if (!backSize.equals(getSize()))
1008N/A newBackBuffer();
1008N/A backGC.setColor(getBackground());
1008N/A backGC.fillRect(0,0,getSize().width,getSize().height);
2517N/A md.paint(backGC);
2517N/A g.drawImage(backBuffer, 0, 0, this);
1008N/A }
1008N/A else
1008N/A md.paint(g);
1008N/A setPainted();
1008N/A } else if (message != null) {
2499N/A g.drawString("Error in model:", 3, 20);
1008N/A g.drawString(message, 10, 40);
1008N/A }
1008N/A }
1008N/A private synchronized void setPainted() {
1008N/A painted = true;
1008N/A notifyAll();
1008N/A }
1008N/A
1008N/A private synchronized void waitPainted()
1008N/A {
1008N/A while (!painted)
1008N/A {
1008N/A try
1008N/A {
1008N/A wait();
1008N/A }
1008N/A catch (InterruptedException e) {}
1008N/A }
1008N/A painted = false;
1008N/A }
1008N/A
1008N/A public String getAppletInfo() {
1008N/A return "Title: XYZApp \nAuthor: James Gosling \nAn applet to put a Chemical model into a page.";
1008N/A }
1008N/A
1008N/A public String[][] getParameterInfo() {
1008N/A String[][] info = {
1008N/A {"model", "path string", "The path to the model to be displayed in .xyz format (see http://chem.leeds.ac.uk/Project/MIME.html). Default is model.obj."},
1008N/A {"scale", "float", "Scale factor. Default is 1 (i.e. no scale)."}
1008N/A };
1008N/A return info;
1008N/A }
1008N/A} // end class XYZApp
1008N/A
1008N/Aclass Atom {
1008N/A private static Applet applet;
1008N/A private static byte[] data;
1008N/A private final static int R = 40;
1008N/A private final static int hx = 15;
1008N/A private final static int hy = 15;
1008N/A private final static int bgGrey = 192;
1008N/A private final static int nBalls = 16;
1008N/A private static int maxr;
1008N/A
1008N/A private int Rl;
1008N/A private int Gl;
1008N/A private int Bl;
1418N/A private Image balls[];
1418N/A
1389N/A static {
1389N/A data = new byte[R * 2 * R * 2];
1008N/A int mr = 0;
1008N/A for (int Y = 2 * R; --Y >= 0;) {
1008N/A int x0 = (int) (Math.sqrt(R * R - (Y - R) * (Y - R)) + 0.5);
1008N/A int p = Y * (R * 2) + R - x0;
1008N/A for (int X = -x0; X < x0; X++) {
1008N/A int x = X + hx;
1008N/A int y = Y - R + hy;
1008N/A int r = (int) (Math.sqrt(x * x + y * y) + 0.5);
1008N/A if (r > mr)
1008N/A mr = r;
1008N/A data[p++] = r <= 0 ? 1 : (byte) r;
1008N/A }
1008N/A }
1008N/A maxr = mr;
1008N/A }
1008N/A static void setApplet(Applet app) {
1008N/A applet = app;
1008N/A }
1008N/A Atom(int Rl, int Gl, int Bl) {
1008N/A this.Rl = Rl;
1008N/A this.Gl = Gl;
1008N/A this.Bl = Bl;
1008N/A }
1008N/A private final int blend(int fg, int bg, float fgfactor) {
1008N/A return (int) (bg + (fg - bg) * fgfactor);
1008N/A }
1008N/A private void Setup() {
1008N/A balls = new Image[nBalls];
1008N/A byte red[] = new byte[256];
2517N/A red[0] = (byte) bgGrey;
2517N/A byte green[] = new byte[256];
2517N/A green[0] = (byte) bgGrey;
2517N/A byte blue[] = new byte[256];
2517N/A blue[0] = (byte) bgGrey;
2517N/A for (int r = 0; r < nBalls; r++) {
2517N/A float b = (float) (r+1) / nBalls;
2517N/A for (int i = maxr; i >= 1; --i) {
2517N/A float d = (float) i / maxr;
2517N/A red[i] = (byte) blend(blend(Rl, 255, d), bgGrey, b);
2517N/A green[i] = (byte) blend(blend(Gl, 255, d), bgGrey, b);
2517N/A blue[i] = (byte) blend(blend(Bl, 255, d), bgGrey, b);
2517N/A }
2517N/A IndexColorModel model = new IndexColorModel(8, maxr + 1,
2517N/A red, green, blue, 0);
2517N/A balls[r] = applet.createImage(
2517N/A new MemoryImageSource(R*2, R*2, model, data, 0, R*2));
2517N/A }
2517N/A }
2517N/A void paint(Graphics gc, int x, int y, int r) {
2517N/A Image ba[] = balls;
2517N/A if (ba == null) {
2517N/A Setup();
2517N/A ba = balls;
2517N/A }
2517N/A Image i = ba[r];
2517N/A int size = 10 + r;
2517N/A gc.drawImage(i, x - (size >> 1), y - (size >> 1), size, size, applet);
2517N/A }
2517N/A}
2517N/A