CCL Home Page
Up Directory CCL nanocad.java
/**
 * nanocad.java
 * Copyright (c) 1997 Will Ware, all rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    or its derived works must display the following acknowledgement:
 * 	This product includes software developed by Will Ware.
 * 
 * This software is provided "as is" and any express or implied warranties,
 * including, but not limited to, the implied warranties of merchantability
 * or fitness for any particular purpose are disclaimed. In no event shall
 * Will Ware be liable for any direct, indirect, incidental, special,
 * exemplary, or consequential damages (including, but not limited to,
 * procurement of substitute goods or services; loss of use, data, or
 * profits; or business interruption) however caused and on any theory of
 * liability, whether in contract, strict liability, or tort (including
 * negligence or otherwise) arising in any way out of the use of this
 * software, even if advised of the possibility of such damage.
 */

import java.applet.*;
import java.awt.*;
import java.lang.Math;
import atom;
import view;
import group;

import aspirin;
import buckybal;
import diamond;
import propane;
import tworings;
import water;

public class nanocad extends Applet
{
  public static final String rcsid =
  "$Id: nanocad.java,v 1.39 1997/09/17 02:40:12 wware Exp $";
  private Button emin;
  private Button clearStruct;
  private Checkbox showForces;
  private Choice knownStructure;
  private Choice whichElement;
  private group grp;
  private Panel drawingArea;
  private int xxx, yyy;
  private atom atom1;
  private double atom1z;
  private boolean inDrawingArea, needToRepaint;
  private Label atomInfoBlab;

  public void clearScreen ()
  {
    Graphics dg = drawingArea.getGraphics();
    Rectangle r = drawingArea.bounds();
    dg.setColor (this.getBackground ());
    dg.fillRect (r.x, r.y, r.width, r.height);
  }
  public void paint (Graphics g)
  {
    clearScreen ();
    grp.updateViewSize ();
    grp.paint ();
    needToRepaint = false;
  }
  private int x0, y0;  // xxx,yyy tracks the mouse, x0,y0 stands still
  private boolean dragFlag = false;
  private int mouseModifiers;
  public void atomInfo ()
  {
    atomInfoBlab.setBackground (this.getBackground ());
    atomInfoBlab.setText ("");
  }
  public void atomInfo (String s)
  {
    atomInfoBlab.setBackground (this.getBackground ());
    atomInfoBlab.setText (s);
  }
  public void atomInfo (atom a)
  {
    if (a == null)
      {
	atomInfoBlab.setBackground (this.getBackground ());
	atomInfoBlab.setText ("");
	return;
      }
    String hinfo = "", bondinfo = "";
    switch (a.hybridization)
      {
      case atom.SP3: hinfo = "sp3"; break;
      case atom.SP2: hinfo = "sp2"; break;
      case atom.SP:  hinfo = "sp"; break;
      }
    if (a.sigmaBonds () < a.maxNumBonds ())
      {
	bondinfo = "";
	atomInfoBlab.setBackground (Color.orange);
      }
    else if (a.sigmaBonds () > a.maxNumBonds ())
      {
	bondinfo = "";
	atomInfoBlab.setBackground (Color.orange);
      }
    else
      atomInfoBlab.setBackground (this.getBackground ());
    atomInfoBlab.setText (a.name() + " " +
			    a.symbol() + " " +
			    hinfo + " " + bondinfo);
  }
  public boolean mouseDown (Event e, int x, int y)
  {
    Rectangle r = drawingArea.bounds();
    inDrawingArea = y < r.height;
    needToRepaint = false;
    double[] scrPos = { x, y, 0 };
    atom1 = grp.selectedAtom (scrPos, true);
    dragFlag = false;
    // We only care about the SHIFT and CTRL modifiers, mask out all others
    mouseModifiers = e.modifiers & (Event.SHIFT_MASK | Event.CTRL_MASK);
    if (atom1 != null)
      {
	double[] atomScrPos = grp.v.xyzToScreen (atom1.x);
	atom1z = atomScrPos[2];
      }
    else
      {
	atom1z = 0;
      }
    atomInfo (atom1);
    xxx = x; yyy = y;
    x0 = x; y0 = y;
    return true;
  }
  public boolean mouseDrag (Event e, int x, int y)
  {
    boolean movingAtom = false;  // if moving atom, no need for extra line
    if (!dragFlag)
      if (x < x0 - 2 || x > x0 + 2 || y < y0 - 2 || y > y0 + 2)
	dragFlag = true;
    if (dragFlag)
      {
	needToRepaint = true;
	if (atom1 == null)
	  {
	    switch (mouseModifiers)
	      {
	      default:
		grp.v.rotate (0.01 * (x - xxx), 0.01 * (y - yyy));
		break;
	      case Event.SHIFT_MASK:
		// grp.forceMultiplier *= Math.exp (0.01 * (x - xxx));
		grp.v.pan (x - xxx, y - yyy);
		break;
	      case Event.CTRL_MASK:
		grp.v.zoomFactor *= Math.exp (0.01 * (x - xxx));
		grp.v.perspDist *= Math.exp (0.01 * (y - yyy));
		break;
	      }
	    clearScreen ();
	    grp.wireframePaint ();
	  }
	else
	  {
	    switch (mouseModifiers)
	      {
	      default:
		double[] scrPos = { x, y, atom1z };
		atom1.x = grp.v.screenToXyz (scrPos);
		movingAtom = true;
		clearScreen ();
		grp.wireframePaint ();
		break;
	      case Event.SHIFT_MASK:
		clearScreen ();
		grp.bubblePaint ();
		break;
	      case Event.CTRL_MASK:
		clearScreen ();
		grp.bubblePaint ();
		break;
	      }
	  }
	xxx = x; yyy = y;
	if (atom1 != null && !movingAtom)
	  grp.drawLineToAtom (atom1, x, y);
      }
    return true;
  }
  public boolean mouseUp (Event e, int x, int y)
  {
    double[] scrPos = { x, y, atom1z };
    Graphics g = this.getGraphics ();
    atom atom2 = grp.selectedAtom (scrPos, false);
    if (atom1 != null)
      {
	if (dragFlag)
	  {
	    // we dragged on an atom
	    switch (mouseModifiers)
	      {
	      default:
		atom1.x = grp.v.screenToXyz (scrPos);
		atom2 = atom1;
		break;
	      case Event.SHIFT_MASK:
		if (atom1 != atom2 && atom1 != null && atom2 != null)
		  {
		    // create a new bond if none exists, or increment the
		    // order if it does exist.
		    grp.addBond (atom1, atom2);
		  }
		break;
	      case Event.CTRL_MASK:
		if (atom1 != atom2 && atom1 != null && atom2 != null)
		  grp.deleteBond (atom1, atom2);
		break;
	      }
	    // give information about the last atom we visited
	    atomInfo (atom2);
	  }
	else
	  {
	    // we clicked on an atom
	    switch (mouseModifiers)
	      {
	      default:
		// atom info, do nothing here
		break;
	      case Event.SHIFT_MASK:
		needToRepaint = true;
		if (atom1 != null)
		  grp.deleteAtom (atom1);
		atomInfo ();
		break;
	      case Event.CTRL_MASK:
		// do nothing for undoLastOp
		break;
	      }
	  }
      }
    else if (!dragFlag)
      {
	// we clicked on air
	switch (mouseModifiers)
	  {
	  default:
	    break;
	  case Event.SHIFT_MASK:
	    int sp;
	    atom a;
	    needToRepaint = true;
	    String ename = whichElement.getSelectedItem ();
	    if (ename.compareTo ("Carbon") == 0)
	      a = new carbon ();
	    else if (ename.compareTo ("Nitrogen") == 0)
	      a = new nitrogen ();
	    else if (ename.compareTo ("Oxygen") == 0)
	      a = new oxygen ();
	    else
	      a = new hydrogen ();
	    grp.addAtom (a, scrPos);
	    atomInfo (a);
	    break;
	  case Event.CTRL_MASK:
	    needToRepaint = true;
	    atomInfo ();
	    grp.updateViewSize ();
	    grp.centerAtoms ();
	    break;
	  }
      }
    if (needToRepaint)
      repaint ();
    return true;
  }
  private void energyMinimize ()
  {
    double scale;
    for (scale = 0.1; scale > 0.0001; scale *= 0.9)
      grp.energyMinimizeStep (scale);
    repaint();
  }
  public boolean action (Event e, Object arg)
  {
    String s;
    if (e.target == emin)
      {
	energyMinimize ();
	return true;
      }
    else if (e.target == clearStruct)
      {
	grp = new group (drawingArea);
	grp.setShowForces (showForces.getState());
	clearScreen ();
	return true;
      }
    else if (e.target == showForces)
      {
	grp.setShowForces (showForces.getState());
	repaint();
	return true;
      }
    else if (e.target == knownStructure)
      {
	switch (knownStructure.getSelectedIndex ())
	  {
	  default:
	  case 0: grp = new aspirin (drawingArea); break;
	  case 1: grp = new buckybal (drawingArea); break;
	  case 2: grp = new diamond (drawingArea); break;
	  case 3: grp = new propane (drawingArea); break;
	  case 4: grp = new tworings (drawingArea); break;
	  case 5: grp = new water (drawingArea); break;
	  }
	grp.setShowForces (showForces.getState());
	repaint ();
	return true;
      }
    return false;
  }
  private void constrain (Container container, Component component,
			  int gridX, int gridY, int gridW, int gridH)
  {
    constrain (container, component, gridX, gridY, gridW, gridH,
	       GridBagConstraints.NONE, GridBagConstraints.NORTHWEST,
	       0.0, 0.0, 0, 0, 0, 0);
  }
  private void constrain (Container container, Component component,
			  int gridX, int gridY, int gridW, int gridH,
			  int fill, int anchor, double weightX,
			  double weightY, int top, int left, int bottom,
			  int right)
  {
    GridBagConstraints c = new GridBagConstraints ();
    c.gridx = gridX; c.gridy = gridY;
    c.gridwidth = gridW; c.gridheight = gridH;
    c.fill = fill; c.anchor = anchor;
    c.weightx = weightX; c.weighty = weightY;
    if (top + bottom + left + right > 0)
      c.insets = new Insets (top, left, bottom, right);
    ((GridBagLayout) container.getLayout()).setConstraints(component, c);
  }
  public void init ()
  {
    Panel controls = new Panel ();
    drawingArea = new Panel ();

    GridBagLayout gridbag = new GridBagLayout ();
    this.setLayout (gridbag);

    // this is kind of kludgey, but if I don't put drawingArea first
    // in the gridbag, I don't know how to correctly translate the
    // coordinates between this applet and the drawingArea panel.
    constrain (this, drawingArea, 0, 0, 1, 1,
	       GridBagConstraints.BOTH,
	       GridBagConstraints.NORTH,
	       1.0, 1.0, 0, 0, 0, 0);

    clearStruct = new Button ("Clear");

    showForces = new Checkbox ("Forces");
    showForces.setState (false);

    emin = new Button ("Energy Minimize");

    knownStructure = new Choice ();
    knownStructure.addItem ("aspirin");
    knownStructure.addItem ("buckyball");
    knownStructure.addItem ("diamond");
    knownStructure.addItem ("propane");
    knownStructure.addItem ("two rings");
    knownStructure.addItem ("water");
    knownStructure.select ("aspirin");

    whichElement = new Choice ();
    whichElement.addItem ("Carbon");
    whichElement.addItem ("Hydrogen");
    whichElement.addItem ("Oxygen");
    whichElement.addItem ("Nitrogen");
    whichElement.select ("Carbon");

    grp = new aspirin (drawingArea);
    grp.v.zoomFactor = 25;

    controls.add (clearStruct);
    controls.add (showForces);
    controls.add (emin);
    controls.add (knownStructure);
    controls.add (whichElement);
    constrain (this, controls, 0, 1, 1, 1);

    TextArea instrucs = new TextArea (5, 100);
    instrucs.setText (
"Mouse operations (S=shift, C=control) / pan: S-drag air\n"+
"rotate: drag air / zoom: C-drag air horiz / move atom: drag atom\n"+
"atom info: click atom / add atom: S-click air / delete atom: S-click atom\n"+
"recenter: C-click air / perspective: C-drag air vert\n"+
"add bond: S-drag atom to atom / delete bond: C-drag atom to atom\n"+
"\n"+
"Double and triple bonds: do repeated add-bonds between the same pair of atoms\n"+
"Expected numbers of bonds: carbon 4, hydrogen 1, nitrogen 3, oxygen 2\n"+
"\n"+
"'Energy Minimize' will take a long time for large structures. Please be patient.\n"+
"It will also take a long time the first time after you add or delete atoms or\n"+
"or bonds, or after you load a new structure. It needs to figure out the energy\n"+
"terms for the new structure, which takes time.\n"+
"\n"+
"Long-range forces (electrostatic and van-der-Waals) are not implemented yet\n"+
"\n"+
"==========================================================================\n"+
"NanoCAD   August 1997\n"+
"Copyright (c) 1997 Will Ware, all rights reserved.\n"+
"\n"+
"Redistribution and use in source and binary forms, with or without\n"+
"modification, are permitted provided that the following conditions\n"+
"are met:\n"+
"1. Redistributions of source code must retain the above copyright\n"+
"   notice, this list of conditions and the following disclaimer.\n"+
"2. Redistributions in binary form must reproduce the above copyright\n"+
"   notice, this list of conditions and the following disclaimer in the\n"+
"   documentation and other materials provided with the distribution.\n"+
"3. All advertising materials mentioning features or use of this software\n"+
"   or its derived works must display the following acknowledgement:\n"+
"	This product includes software developed by Will Ware.\n"+
"\n"+
"This software is provided \"as is\" and any express or implied warranties,\n"+
"including, but not limited to, the implied warranties of merchantability\n"+
"or fitness for any particular purpose are disclaimed. In no event shall\n"+
"Will Ware be liable for any direct, indirect, incidental, special,\n"+
"exemplary, or consequential damages (including, but not limited to,\n"+
"procurement of substitute goods or services; loss of use, data, or\n"+
"profits; or business interruption) however caused and on any theory of\n"+
"liability, whether in contract, strict liability, or tort (including\n"+
"negligence or otherwise) arising in any way out of the use of this\n"+
"software, even if advised of the possibility of such damage.\n"
);
    instrucs.setEditable (false);
    constrain (this, instrucs, 0, 2, 1, 1);

    String spaces = "                              "; /* 30 spaces */
    atomInfoBlab = new Label (spaces + spaces + spaces + spaces + spaces);
    constrain (this, atomInfoBlab, 0, 3, 1, 1);

    this.add (drawingArea);
    this.add (controls);
    this.add (instrucs);
    this.add (atomInfoBlab);

    this.repaint ();
    this.show ();
  }
}
Modified: Sat Jan 17 17:00:00 1998 GMT
Page accessed 4405 times since Sat May 22 21:49:45 1999 GMT