Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/applets/MoleculeViewer/XYZApp.java
38827 views
/*1* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/38394041import java.applet.Applet;42import java.awt.Image;43import java.awt.Graphics;44import java.awt.Dimension;45import java.awt.event.MouseEvent;46import java.awt.event.MouseListener;47import java.awt.event.MouseMotionListener;48import java.net.URL;49import java.awt.image.IndexColorModel;50import java.awt.image.MemoryImageSource;51import java.io.BufferedReader;52import java.io.IOException;53import java.io.InputStream;54import java.io.InputStreamReader;55import java.io.StreamTokenizer;56import java.util.HashMap;57import java.util.Map;58import java.util.logging.Level;59import java.util.logging.Logger;606162/*63* A set of classes to parse, represent and display Chemical compounds in64* .xyz format (see http://chem.leeds.ac.uk/Project/MIME.html)65*/66/** The representation of a Chemical .xyz model */67final class XYZChemModel {6869float vert[];70Atom atoms[];71int tvert[];72int ZsortMap[];73int nvert, maxvert;74static final Map<String, Atom> atomTable = new HashMap<String, Atom>();75static Atom defaultAtom;7677static {78atomTable.put("c", new Atom(0, 0, 0));79atomTable.put("h", new Atom(210, 210, 210));80atomTable.put("n", new Atom(0, 0, 255));81atomTable.put("o", new Atom(255, 0, 0));82atomTable.put("p", new Atom(255, 0, 255));83atomTable.put("s", new Atom(255, 255, 0));84atomTable.put("hn", new Atom(150, 255, 150)); /* !!*/85defaultAtom = new Atom(255, 100, 200);86}87boolean transformed;88Matrix3D mat;89float xmin, xmax, ymin, ymax, zmin, zmax;9091XYZChemModel() {92mat = new Matrix3D();93mat.xrot(20);94mat.yrot(30);95}9697/** Create a Chemical model by parsing an input stream */98XYZChemModel(InputStream is) throws Exception {99this();100StreamTokenizer st = new StreamTokenizer(101new BufferedReader(new InputStreamReader(is, "UTF-8")));102st.eolIsSignificant(true);103st.commentChar('#');104105try {106scan:107while (true) {108switch (st.nextToken()) {109case StreamTokenizer.TT_EOF:110break scan;111default:112break;113case StreamTokenizer.TT_WORD:114String name = st.sval;115double x = 0,116y = 0,117z = 0;118if (st.nextToken() == StreamTokenizer.TT_NUMBER) {119x = st.nval;120if (st.nextToken() == StreamTokenizer.TT_NUMBER) {121y = st.nval;122if (st.nextToken() == StreamTokenizer.TT_NUMBER) {123z = st.nval;124}125}126}127addVert(name, (float) x, (float) y, (float) z);128while (st.ttype != StreamTokenizer.TT_EOL129&& st.ttype != StreamTokenizer.TT_EOF) {130st.nextToken();131}132133} // end Switch134135} // end while136137is.close();138139} // end Try140catch (IOException e) {141}142143if (st.ttype != StreamTokenizer.TT_EOF) {144throw new Exception(st.toString());145}146147} // end XYZChemModel()148149/** Add a vertex to this model */150int addVert(String name, float x, float y, float z) {151int i = nvert;152if (i >= maxvert) {153if (vert == null) {154maxvert = 100;155vert = new float[maxvert * 3];156atoms = new Atom[maxvert];157} else {158maxvert *= 2;159float nv[] = new float[maxvert * 3];160System.arraycopy(vert, 0, nv, 0, vert.length);161vert = nv;162Atom na[] = new Atom[maxvert];163System.arraycopy(atoms, 0, na, 0, atoms.length);164atoms = na;165}166}167Atom a = atomTable.get(name.toLowerCase());168if (a == null) {169a = defaultAtom;170}171atoms[i] = a;172i *= 3;173vert[i] = x;174vert[i + 1] = y;175vert[i + 2] = z;176return nvert++;177}178179/** Transform all the points in this model */180void transform() {181if (transformed || nvert <= 0) {182return;183}184if (tvert == null || tvert.length < nvert * 3) {185tvert = new int[nvert * 3];186}187mat.transform(vert, tvert, nvert);188transformed = true;189}190191/** Paint this model to a graphics context. It uses the matrix associated192with this model to map from model space to screen space.193The next version of the browser should have double buffering,194which will make this *much* nicer */195void paint(Graphics g) {196if (vert == null || nvert <= 0) {197return;198}199transform();200int v[] = tvert;201int zs[] = ZsortMap;202if (zs == null) {203ZsortMap = zs = new int[nvert];204for (int i = nvert; --i >= 0;) {205zs[i] = i * 3;206}207}208209/*210* I use a bubble sort since from one iteration to the next, the sort211* order is pretty stable, so I just use what I had last time as a212* "guess" of the sorted order. With luck, this reduces O(N log N)213* to O(N)214*/215216for (int i = nvert - 1; --i >= 0;) {217boolean flipped = false;218for (int j = 0; j <= i; j++) {219int a = zs[j];220int b = zs[j + 1];221if (v[a + 2] > v[b + 2]) {222zs[j + 1] = a;223zs[j] = b;224flipped = true;225}226}227if (!flipped) {228break;229}230}231232int lim = nvert;233if (lim <= 0 || nvert <= 0) {234return;235}236for (int i = 0; i < lim; i++) {237int j = zs[i];238int grey = v[j + 2];239if (grey < 0) {240grey = 0;241}242if (grey > 15) {243grey = 15;244}245// g.drawString(names[i], v[j], v[j+1]);246atoms[j / 3].paint(g, v[j], v[j + 1], grey);247// g.drawImage(iBall, v[j] - (iBall.width >> 1), v[j + 1] -248// (iBall.height >> 1));249}250}251252/** Find the bounding box of this model */253void findBB() {254if (nvert <= 0) {255return;256}257float v[] = vert;258float _xmin = v[0], _xmax = _xmin;259float _ymin = v[1], _ymax = _ymin;260float _zmin = v[2], _zmax = _zmin;261for (int i = nvert * 3; (i -= 3) > 0;) {262float x = v[i];263if (x < _xmin) {264_xmin = x;265}266if (x > _xmax) {267_xmax = x;268}269float y = v[i + 1];270if (y < _ymin) {271_ymin = y;272}273if (y > _ymax) {274_ymax = y;275}276float z = v[i + 2];277if (z < _zmin) {278_zmin = z;279}280if (z > _zmax) {281_zmax = z;282}283}284this.xmax = _xmax;285this.xmin = _xmin;286this.ymax = _ymax;287this.ymin = _ymin;288this.zmax = _zmax;289this.zmin = _zmin;290}291}292293294/** An applet to put a Chemical model into a page */295@SuppressWarnings("serial")296public class XYZApp extends Applet implements Runnable, MouseListener,297MouseMotionListener {298299XYZChemModel md;300boolean painted = true;301float xfac;302int prevx, prevy;303float scalefudge = 1;304Matrix3D amat = new Matrix3D(), tmat = new Matrix3D();305String mdname = null;306String message = null;307Image backBuffer;308Graphics backGC;309Dimension backSize;310311private synchronized void newBackBuffer() {312backBuffer = createImage(getSize().width, getSize().height);313if (backGC != null) {314backGC.dispose();315}316backGC = backBuffer.getGraphics();317backSize = getSize();318}319320@Override321public void init() {322mdname = getParameter("model");323try {324scalefudge = Float.valueOf(getParameter("scale")).floatValue();325} catch (Exception ignored) {326}327amat.yrot(20);328amat.xrot(20);329if (mdname == null) {330mdname = "model.obj";331}332resize(getSize().width <= 20 ? 400 : getSize().width,333getSize().height <= 20 ? 400 : getSize().height);334newBackBuffer();335addMouseListener(this);336addMouseMotionListener(this);337}338339@Override340public void destroy() {341removeMouseListener(this);342removeMouseMotionListener(this);343}344345@Override346public void run() {347InputStream is = null;348try {349Thread.currentThread().setPriority(Thread.MIN_PRIORITY);350is = getClass().getResourceAsStream(mdname);351XYZChemModel m = new XYZChemModel(is);352Atom.setApplet(this);353md = m;354m.findBB();355float xw = m.xmax - m.xmin;356float yw = m.ymax - m.ymin;357float zw = m.zmax - m.zmin;358if (yw > xw) {359xw = yw;360}361if (zw > xw) {362xw = zw;363}364float f1 = getSize().width / xw;365float f2 = getSize().height / xw;366xfac = 0.7f * (f1 < f2 ? f1 : f2) * scalefudge;367} catch (Exception e) {368Logger.getLogger(XYZApp.class.getName()).log(Level.SEVERE, null, e);369md = null;370message = e.toString();371}372try {373if (is != null) {374is.close();375}376} catch (Exception ignored) {377}378repaint();379}380381@Override382public void start() {383if (md == null && message == null) {384new Thread(this).start();385}386}387388@Override389public void stop() {390}391/* event handling */392393@Override394public void mouseClicked(MouseEvent e) {395}396397@Override398public void mousePressed(MouseEvent e) {399prevx = e.getX();400prevy = e.getY();401e.consume();402}403404@Override405public void mouseReleased(MouseEvent e) {406}407408@Override409public void mouseEntered(MouseEvent e) {410}411412@Override413public void mouseExited(MouseEvent e) {414}415416@Override417public void mouseDragged(MouseEvent e) {418int x = e.getX();419int y = e.getY();420tmat.unit();421float xtheta = (prevy - y) * (360.0f / getSize().width);422float ytheta = (x - prevx) * (360.0f / getSize().height);423tmat.xrot(xtheta);424tmat.yrot(ytheta);425amat.mult(tmat);426if (painted) {427painted = false;428repaint();429}430prevx = x;431prevy = y;432e.consume();433}434435@Override436public void mouseMoved(MouseEvent e) {437}438439@Override440public void update(Graphics g) {441if (backBuffer == null) {442g.clearRect(0, 0, getSize().width, getSize().height);443}444paint(g);445}446447@Override448public void paint(Graphics g) {449if (md != null) {450md.mat.unit();451md.mat.translate(-(md.xmin + md.xmax) / 2,452-(md.ymin + md.ymax) / 2,453-(md.zmin + md.zmax) / 2);454md.mat.mult(amat);455// md.mat.scale(xfac, -xfac, 8 * xfac / getSize().width);456md.mat.scale(xfac, -xfac, 16 * xfac / getSize().width);457md.mat.translate(getSize().width / 2, getSize().height / 2, 8);458md.transformed = false;459if (backBuffer != null) {460if (!backSize.equals(getSize())) {461newBackBuffer();462}463backGC.setColor(getBackground());464backGC.fillRect(0, 0, getSize().width, getSize().height);465md.paint(backGC);466g.drawImage(backBuffer, 0, 0, this);467} else {468md.paint(g);469}470setPainted();471} else if (message != null) {472g.drawString("Error in model:", 3, 20);473g.drawString(message, 10, 40);474}475}476477private synchronized void setPainted() {478painted = true;479notifyAll();480}481482@Override483public String getAppletInfo() {484return "Title: XYZApp \nAuthor: James Gosling \nAn applet to put"485+ " a Chemical model into a page.";486}487488@Override489public String[][] getParameterInfo() {490String[][] info = {491{ "model", "path string", "The path to the model to be displayed"492+ " in .xyz format "493+ "(see http://chem.leeds.ac.uk/Project/MIME.html)."494+ " Default is model.obj." },495{ "scale", "float", "Scale factor. Default is 1 (i.e. no scale)." }496};497return info;498}499} // end class XYZApp500501502class Atom {503504private static Applet applet;505private static byte[] data;506private final static int R = 40;507private final static int hx = 15;508private final static int hy = 15;509private final static int bgGrey = 192;510private final static int nBalls = 16;511private static int maxr;512private int Rl;513private int Gl;514private int Bl;515private Image balls[];516517static {518data = new byte[R * 2 * R * 2];519int mr = 0;520for (int Y = 2 * R; --Y >= 0;) {521int x0 = (int) (Math.sqrt(R * R - (Y - R) * (Y - R)) + 0.5);522int p = Y * (R * 2) + R - x0;523for (int X = -x0; X < x0; X++) {524int x = X + hx;525int y = Y - R + hy;526int r = (int) (Math.sqrt(x * x + y * y) + 0.5);527if (r > mr) {528mr = r;529}530data[p++] = r <= 0 ? 1 : (byte) r;531}532}533maxr = mr;534}535536static void setApplet(Applet app) {537applet = app;538}539540Atom(int Rl, int Gl, int Bl) {541this.Rl = Rl;542this.Gl = Gl;543this.Bl = Bl;544}545546private int blend(int fg, int bg, float fgfactor) {547return (int) (bg + (fg - bg) * fgfactor);548}549550private void Setup() {551balls = new Image[nBalls];552byte red[] = new byte[256];553red[0] = (byte) bgGrey;554byte green[] = new byte[256];555green[0] = (byte) bgGrey;556byte blue[] = new byte[256];557blue[0] = (byte) bgGrey;558for (int r = 0; r < nBalls; r++) {559float b = (float) (r + 1) / nBalls;560for (int i = maxr; i >= 1; --i) {561float d = (float) i / maxr;562red[i] = (byte) blend(blend(Rl, 255, d), bgGrey, b);563green[i] = (byte) blend(blend(Gl, 255, d), bgGrey, b);564blue[i] = (byte) blend(blend(Bl, 255, d), bgGrey, b);565}566IndexColorModel model = new IndexColorModel(8, maxr + 1,567red, green, blue, 0);568balls[r] = applet.createImage(569new MemoryImageSource(R * 2, R * 2, model, data, 0, R * 2));570}571}572573void paint(Graphics gc, int x, int y, int r) {574Image ba[] = balls;575if (ba == null) {576Setup();577ba = balls;578}579Image i = ba[r];580int size = 10 + r;581gc.drawImage(i, x - (size >> 1), y - (size >> 1), size, size, applet);582}583}584585586