blob: 6f7cd0c5f0232a0d963cd6a0363982cd6d477ec2 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Sun Microsystems nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 */
34
35/*
36 * A set of classes to parse, represent and display Chemical compounds in
37 * .xyz format (see http://chem.leeds.ac.uk/Project/MIME.html)
38 */
39
40import java.applet.Applet;
41import java.awt.Image;
42import java.awt.Event;
43import java.awt.Graphics;
44import java.awt.Dimension;
45import java.io.*;
46import java.net.URL;
47import java.util.Hashtable;
48import java.awt.image.IndexColorModel;
49import java.awt.image.ColorModel;
50import java.awt.image.MemoryImageSource;
51import java.awt.event.*;
52
53/** The representation of a Chemical .xyz model */
54class XYZChemModel {
55 float vert[];
56 Atom atoms[];
57 int tvert[];
58 int ZsortMap[];
59 int nvert, maxvert;
60
61 static Hashtable atomTable = new Hashtable();
62 static Atom defaultAtom;
63 static {
64 atomTable.put("c", new Atom(0, 0, 0));
65 atomTable.put("h", new Atom(210, 210, 210));
66 atomTable.put("n", new Atom(0, 0, 255));
67 atomTable.put("o", new Atom(255, 0, 0));
68 atomTable.put("p", new Atom(255, 0, 255));
69 atomTable.put("s", new Atom(255, 255, 0));
70 atomTable.put("hn", new Atom(150, 255, 150)); /* !!*/
71 defaultAtom = new Atom(255, 100, 200);
72 }
73
74 boolean transformed;
75 Matrix3D mat;
76
77 float xmin, xmax, ymin, ymax, zmin, zmax;
78
79
80 XYZChemModel () {
81 mat = new Matrix3D();
82 mat.xrot(20);
83 mat.yrot(30);
84 }
85
86
87 /** Create a Cehmical model by parsing an input stream */
88 XYZChemModel (InputStream is) throws Exception
89 {
90 this();
91 StreamTokenizer st = new StreamTokenizer(
92 new BufferedReader(new InputStreamReader(is, "UTF-8")));
93 st.eolIsSignificant(true);
94 st.commentChar('#');
95 int slot = 0;
96
97 try
98 {
99scan:
100 while (true)
101 {
102 switch ( st.nextToken() )
103 {
104 case StreamTokenizer.TT_EOF:
105 break scan;
106 default:
107 break;
108 case StreamTokenizer.TT_WORD:
109 String name = st.sval;
110 double x = 0, y = 0, z = 0;
111 if (st.nextToken() == StreamTokenizer.TT_NUMBER)
112 {
113 x = st.nval;
114 if (st.nextToken() == StreamTokenizer.TT_NUMBER)
115 {
116 y = st.nval;
117 if (st.nextToken() == StreamTokenizer.TT_NUMBER)
118 z = st.nval;
119 }
120 }
121 addVert(name, (float) x, (float) y, (float) z);
122 while( st.ttype != StreamTokenizer.TT_EOL &&
123 st.ttype != StreamTokenizer.TT_EOF )
124 st.nextToken();
125
126 } // end Switch
127
128 } // end while
129
130 is.close();
131
132 } // end Try
133 catch( IOException e) {}
134
135 if (st.ttype != StreamTokenizer.TT_EOF)
136 throw new Exception(st.toString());
137
138 } // end XYZChemModel()
139
140 /** Add a vertex to this model */
141 int addVert(String name, float x, float y, float z) {
142 int i = nvert;
143 if (i >= maxvert)
144 if (vert == null) {
145 maxvert = 100;
146 vert = new float[maxvert * 3];
147 atoms = new Atom[maxvert];
148 } else {
149 maxvert *= 2;
150 float nv[] = new float[maxvert * 3];
151 System.arraycopy(vert, 0, nv, 0, vert.length);
152 vert = nv;
153 Atom na[] = new Atom[maxvert];
154 System.arraycopy(atoms, 0, na, 0, atoms.length);
155 atoms = na;
156 }
157 Atom a = (Atom) atomTable.get(name.toLowerCase());
158 if (a == null) a = defaultAtom;
159 atoms[i] = a;
160 i *= 3;
161 vert[i] = x;
162 vert[i + 1] = y;
163 vert[i + 2] = z;
164 return nvert++;
165 }
166
167 /** Transform all the points in this model */
168 void transform() {
169 if (transformed || nvert <= 0)
170 return;
171 if (tvert == null || tvert.length < nvert * 3)
172 tvert = new int[nvert * 3];
173 mat.transform(vert, tvert, nvert);
174 transformed = true;
175 }
176
177
178 /** Paint this model to a graphics context. It uses the matrix associated
179 with this model to map from model space to screen space.
180 The next version of the browser should have double buffering,
181 which will make this *much* nicer */
182 void paint(Graphics g) {
183 if (vert == null || nvert <= 0)
184 return;
185 transform();
186 int v[] = tvert;
187 int zs[] = ZsortMap;
188 if (zs == null) {
189 ZsortMap = zs = new int[nvert];
190 for (int i = nvert; --i >= 0;)
191 zs[i] = i * 3;
192 }
193
194 /*
195 * I use a bubble sort since from one iteration to the next, the sort
196 * order is pretty stable, so I just use what I had last time as a
197 * "guess" of the sorted order. With luck, this reduces O(N log N)
198 * to O(N)
199 */
200
201 for (int i = nvert - 1; --i >= 0;) {
202 boolean flipped = false;
203 for (int j = 0; j <= i; j++) {
204 int a = zs[j];
205 int b = zs[j + 1];
206 if (v[a + 2] > v[b + 2]) {
207 zs[j + 1] = a;
208 zs[j] = b;
209 flipped = true;
210 }
211 }
212 if (!flipped)
213 break;
214 }
215
216 int lg = 0;
217 int lim = nvert;
218 Atom ls[] = atoms;
219 if (lim <= 0 || nvert <= 0)
220 return;
221 for (int i = 0; i < lim; i++) {
222 int j = zs[i];
223 int grey = v[j + 2];
224 if (grey < 0)
225 grey = 0;
226 if (grey > 15)
227 grey = 15;
228 // g.drawString(names[i], v[j], v[j+1]);
229 atoms[j/3].paint(g, v[j], v[j + 1], grey);
230 // g.drawImage(iBall, v[j] - (iBall.width >> 1), v[j + 1] -
231 // (iBall.height >> 1));
232 }
233 }
234
235 /** Find the bounding box of this model */
236 void findBB() {
237 if (nvert <= 0)
238 return;
239 float v[] = vert;
240 float xmin = v[0], xmax = xmin;
241 float ymin = v[1], ymax = ymin;
242 float zmin = v[2], zmax = zmin;
243 for (int i = nvert * 3; (i -= 3) > 0;) {
244 float x = v[i];
245 if (x < xmin)
246 xmin = x;
247 if (x > xmax)
248 xmax = x;
249 float y = v[i + 1];
250 if (y < ymin)
251 ymin = y;
252 if (y > ymax)
253 ymax = y;
254 float z = v[i + 2];
255 if (z < zmin)
256 zmin = z;
257 if (z > zmax)
258 zmax = z;
259 }
260 this.xmax = xmax;
261 this.xmin = xmin;
262 this.ymax = ymax;
263 this.ymin = ymin;
264 this.zmax = zmax;
265 this.zmin = zmin;
266 }
267}
268
269/** An applet to put a Chemical model into a page */
270public class XYZApp
271 extends Applet
272 implements Runnable, MouseListener, MouseMotionListener {
273 XYZChemModel md;
274 boolean painted = true;
275 float xfac;
276 int prevx, prevy;
277 float xtheta, ytheta;
278 float scalefudge = 1;
279 Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();
280 String mdname = null;
281 String message = null;
282 Image backBuffer;
283 Graphics backGC;
284 Dimension backSize;
285
286
287 private synchronized void newBackBuffer() {
288 backBuffer = createImage(getSize().width, getSize().height);
289 if (backGC != null) {
290 backGC.dispose();
291 }
292 backGC = backBuffer.getGraphics();
293 backSize = getSize();
294 }
295
296 public void init() {
297 mdname = getParameter("model");
298 try {
299 scalefudge = Float.valueOf(getParameter("scale")).floatValue();
300 } catch(Exception e) {
301 };
302 amat.yrot(20);
303 amat.xrot(20);
304 if (mdname == null)
305 mdname = "model.obj";
306 resize(getSize().width <= 20 ? 400 : getSize().width,
307 getSize().height <= 20 ? 400 : getSize().height);
308 newBackBuffer();
309 addMouseListener(this);
310 addMouseMotionListener(this);
311 }
312
313 public void destroy() {
314 removeMouseListener(this);
315 removeMouseMotionListener(this);
316 }
317
318 public void run() {
319 InputStream is = null;
320 try {
321 Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
322 is = new URL(getDocumentBase(), mdname).openStream();
323 XYZChemModel m = new XYZChemModel (is);
324 Atom.setApplet(this);
325 md = m;
326 m.findBB();
327 float xw = m.xmax - m.xmin;
328 float yw = m.ymax - m.ymin;
329 float zw = m.zmax - m.zmin;
330 if (yw > xw)
331 xw = yw;
332 if (zw > xw)
333 xw = zw;
334 float f1 = getSize().width / xw;
335 float f2 = getSize().height / xw;
336 xfac = 0.7f * (f1 < f2 ? f1 : f2) * scalefudge;
337 } catch(Exception e) {
338 e.printStackTrace();
339 md = null;
340 message = e.toString();
341 }
342 try {
343 if (is != null)
344 is.close();
345 } catch(Exception e) {
346 }
347 repaint();
348 }
349 public void start() {
350 if (md == null && message == null)
351 new Thread(this).start();
352 }
353 public void stop() {
354 }
355 /* event handling */
356 public void mouseClicked(MouseEvent e) {
357 }
358 public void mousePressed(MouseEvent e) {
359 prevx = e.getX();
360 prevy = e.getY();
361 e.consume();
362 }
363 public void mouseReleased(MouseEvent e) {
364 }
365 public void mouseEntered(MouseEvent e) {
366 }
367 public void mouseExited(MouseEvent e) {
368 }
369 public void mouseDragged(MouseEvent e) {
370 int x = e.getX();
371 int y = e.getY();
372 tmat.unit();
373 float xtheta = (prevy - y) * (360.0f / getSize().width);
374 float ytheta = (x - prevx) * (360.0f / getSize().height);
375 tmat.xrot(xtheta);
376 tmat.yrot(ytheta);
377 amat.mult(tmat);
378 if (painted) {
379 painted = false;
380 repaint();
381 }
382 prevx = x;
383 prevy = y;
384 e.consume();
385 }
386 public void mouseMoved(MouseEvent e) {
387 }
388
389 public void update(Graphics g) {
390 if (backBuffer == null)
391 g.clearRect(0, 0, getSize().width, getSize().height);
392 paint(g);
393 }
394
395 public void paint(Graphics g) {
396 if (md != null) {
397 md.mat.unit();
398 md.mat.translate(-(md.xmin + md.xmax) / 2,
399 -(md.ymin + md.ymax) / 2,
400 -(md.zmin + md.zmax) / 2);
401 md.mat.mult(amat);
402 // md.mat.scale(xfac, -xfac, 8 * xfac / getSize().width);
403 md.mat.scale(xfac, -xfac, 16 * xfac / getSize().width);
404 md.mat.translate(getSize().width / 2, getSize().height / 2, 8);
405 md.transformed = false;
406 if (backBuffer != null) {
407 if (!backSize.equals(getSize()))
408 newBackBuffer();
409 backGC.setColor(getBackground());
410 backGC.fillRect(0,0,getSize().width,getSize().height);
411 md.paint(backGC);
412 g.drawImage(backBuffer, 0, 0, this);
413 }
414 else
415 md.paint(g);
416 setPainted();
417 } else if (message != null) {
418 g.drawString("Error in model:", 3, 20);
419 g.drawString(message, 10, 40);
420 }
421 }
422 private synchronized void setPainted() {
423 painted = true;
424 notifyAll();
425 }
426
427 private synchronized void waitPainted()
428 {
429 while (!painted)
430 {
431 try
432 {
433 wait();
434 }
435 catch (InterruptedException e) {}
436 }
437 painted = false;
438 }
439
440 public String getAppletInfo() {
441 return "Title: XYZApp \nAuthor: James Gosling \nAn applet to put a Chemical model into a page.";
442 }
443
444 public String[][] getParameterInfo() {
445 String[][] info = {
446 {"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."},
447 {"scale", "float", "Scale factor. Default is 1 (i.e. no scale)."}
448 };
449 return info;
450 }
451} // end class XYZApp
452
453class Atom {
454 private static Applet applet;
455 private static byte[] data;
456 private final static int R = 40;
457 private final static int hx = 15;
458 private final static int hy = 15;
459 private final static int bgGrey = 192;
460 private final static int nBalls = 16;
461 private static int maxr;
462
463 private int Rl;
464 private int Gl;
465 private int Bl;
466 private Image balls[];
467
468 static {
469 data = new byte[R * 2 * R * 2];
470 int mr = 0;
471 for (int Y = 2 * R; --Y >= 0;) {
472 int x0 = (int) (Math.sqrt(R * R - (Y - R) * (Y - R)) + 0.5);
473 int p = Y * (R * 2) + R - x0;
474 for (int X = -x0; X < x0; X++) {
475 int x = X + hx;
476 int y = Y - R + hy;
477 int r = (int) (Math.sqrt(x * x + y * y) + 0.5);
478 if (r > mr)
479 mr = r;
480 data[p++] = r <= 0 ? 1 : (byte) r;
481 }
482 }
483 maxr = mr;
484 }
485 static void setApplet(Applet app) {
486 applet = app;
487 }
488 Atom(int Rl, int Gl, int Bl) {
489 this.Rl = Rl;
490 this.Gl = Gl;
491 this.Bl = Bl;
492 }
493 private final int blend(int fg, int bg, float fgfactor) {
494 return (int) (bg + (fg - bg) * fgfactor);
495 }
496 private void Setup() {
497 balls = new Image[nBalls];
498 byte red[] = new byte[256];
499 red[0] = (byte) bgGrey;
500 byte green[] = new byte[256];
501 green[0] = (byte) bgGrey;
502 byte blue[] = new byte[256];
503 blue[0] = (byte) bgGrey;
504 for (int r = 0; r < nBalls; r++) {
505 float b = (float) (r+1) / nBalls;
506 for (int i = maxr; i >= 1; --i) {
507 float d = (float) i / maxr;
508 red[i] = (byte) blend(blend(Rl, 255, d), bgGrey, b);
509 green[i] = (byte) blend(blend(Gl, 255, d), bgGrey, b);
510 blue[i] = (byte) blend(blend(Bl, 255, d), bgGrey, b);
511 }
512 IndexColorModel model = new IndexColorModel(8, maxr + 1,
513 red, green, blue, 0);
514 balls[r] = applet.createImage(
515 new MemoryImageSource(R*2, R*2, model, data, 0, R*2));
516 }
517 }
518 void paint(Graphics gc, int x, int y, int r) {
519 Image ba[] = balls;
520 if (ba == null) {
521 Setup();
522 ba = balls;
523 }
524 Image i = ba[r];
525 int size = 10 + r;
526 gc.drawImage(i, x - (size >> 1), y - (size >> 1), size, size, applet);
527 }
528}