CCL Home Page
Up Directory CCL MoleculeEditor_1_0.java
/*
 * @(#)MoleculeEditor_1_0.java     96/05/28 Denis M. BAYADA
 *
 * Copyright (c) 1996 Denis M. BAYADA, University of Leeds, Leeds, U.K.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and
 * without fee is hereby granted.
 * Please email denis@mi.leeds.ac.uk for any questions or comments included
 * questions on commercial availability.
 *
 * This software is given "as is".  I do not accept responsibility
 * for any damage to software or hardware that it may cause.
 */
  
import java.awt.*;
import java.applet.*;
import java.lang.Math;


/* 
 * The main class.  This class is static.  It contains all the interface.
 */

public class MoleculeEditor_1_0 extends java.applet.Applet {

// These static final byte constants are used to check what 
// is the current mode
 
    static final byte SINGLE = 1;   // Single bond drawing
    static final byte DOUBLE = 2;   // Double bond drawing
    static final byte SELECT = 3;   // Selection
    static final byte LASSO = 5;    // Lasso
    static final byte CYCLO6 = 4;   // cyclohexane drawing
    static final byte CHANGEAT = 6; // Changing Atom Type
    static final byte ZOOM = 7;	    // Zoom
    static final byte ARO6 = 8;	    // Aromatic 6
    static final byte TRIPLE = 9;   // Triple bond drawing
    static final byte UNKNOWN = 10; // unknown selection (should never happen)
    static final byte ARO5 = 11;    // Aromatic 5
    static final byte CYCLO5 = 12;  // cyclopentane
    
// Different GUI components

    Panel drawing_area;	// Where the molecule is drawn

    Button draw_single_button, change_atom_type_button;	    // The buttons
    Button draw_double_button, lasso_button, select_button; 
    Button delete_selected_button, zoom_button, draw_triple_button;
    Button merge_button;
    
    Panel button_panel;	// Panel that contains the buttons
    
    Label global_name;	// The Molecule Editor label
    Label version_name;	// The version label
    Label atom_type;	// The atom type changing label
    Choice choice;	// The atom type menu
    ImageLinesButton_1_0 cyclo6, aro6, cyclo5, aro5;	// The drawn buttons
    static String cyclo6Name = new String("Cyclo6");	// name of drawn buttons
    static String aro6Name = new String("Aro6");
    static String cyclo5Name = new String("Cyclo5");
    static String aro5Name = new String("Aro5");

    TextField message_window = new TextField(17);   // The message window

    GridBagLayout gridbag = new GridBagLayout();    // The container layout

// The rest
    Molecule_1_0 aMolecule; // The molecule itself
    static int current_selected = SELECT;   // The variable that is set when
					    // a button is clicked.
					    // It is a static!
    int x,y,oldx,oldy,start_atom;   // variables that store the mouse position
				    // when a button is clicked, the previous
				    // mouse position, the atom on which
				    // the mouse was clicked.
    int atom_highlighted = -1;	    // The atom id that is covered by the mouse
    int bond_highlighted = -1;	    // the bond id that is covered by the mouse
    Polygon lasso_pol;	// The polygon that stores the lasso
    int temp_int=0;	// temporary variables
    int temp_x[] = new int[20];	// temporary variables
    int temp_y[] = new int[20];	// temporary variables
    
    int spare_x[] = new int[100];   // variables used for the ZOOM
    int spare_y[] = new int[100];   // variables used for the ZOOM
    int gravity_x, gravity_y;	// variables used for the ZOOM
    int runner;	// variables used for the ZOOM
    int last_selected = -1; // variables used for the Selection

    boolean inside_drawing_area = false;    // true if mouse in drawing area
    
    

// init function is called once.  It draws the whole interface

    public void init()  {
	Polygon pol;
	
	// Creation of the cyclo6 drawn button:
	int angle = 0;
	for (int i=0; i<6; i++)
	{
	    temp_x[i] = 
		(int)(15.0*Math.cos(60.0*(double)angle/180.0*Math.PI))+15; 
	    temp_y[i] = 
		(int)(15.0*Math.sin(60.0*(double)angle/180.0*Math.PI))+15; 
	    angle++;
	}
	pol = new Polygon(temp_x,temp_y,6);
	cyclo6 = new ImageLinesButton_1_0(pol,cyclo6Name);

	// Creation of the aro6 drawn button:
	temp_x[0] = 30; 
	temp_y[0] = 15;
	temp_x[11] = temp_x[0];
	temp_y[11] = temp_y[0];
	angle = 1;
	for (int i=1; i<11; i+=2)
	{
	    temp_x[i] = 
		(int)(15.0*Math.cos(60.0*(double)angle/180.0*Math.PI))+15; 
	    temp_y[i] = 
		(int)(15.0*Math.sin(60.0*(double)angle/180.0*Math.PI))+15; 
	    angle++;
	    temp_x[i+1] = temp_x[i];
	    temp_y[i+1] = temp_y[i];
	}
	angle = 0;
	for (int i=12; i<18; i++)
	{
	    temp_x[i] = 
		(int)(10.0*Math.cos(60.0*(double)angle/180.0*Math.PI))+15; 
	    temp_y[i] = 
		(int)(10.0*Math.sin(60.0*(double)angle/180.0*Math.PI))+15; 
	    angle++;
	}
	aro6 = new ImageLinesButton_1_0(18, temp_x, temp_y, aro6Name);

	// Creation of the cyclo5 drawn button:
	angle = 0;
	for (int i=0; i<5; i++)
	{
	    temp_x[i] = 
		(int)(15.0*Math.cos(72.0*(double)angle/180.0*Math.PI))+15; 
	    temp_y[i] = 
		(int)(15.0*Math.sin(72.0*(double)angle/180.0*Math.PI))+15; 
	    angle++;
	}
	pol = new Polygon(temp_x,temp_y,5);
	cyclo5 = new ImageLinesButton_1_0(pol,cyclo5Name);

	// Creation of the aro5 drawn button:
	temp_x[0] = 30; 
	temp_y[0] = 15;
	temp_x[9] = temp_x[0];
	temp_y[9] = temp_y[0];
	angle = 1;
	for (int i=1; i<9; i+=2)
	{
	    temp_x[i] = 
		(int)(15.0*Math.cos(72.0*(double)angle/180.0*Math.PI))+15; 
	    temp_y[i] = 
		(int)(15.0*Math.sin(72.0*(double)angle/180.0*Math.PI))+15; 
	    angle++;
	    temp_x[i+1] = temp_x[i];
	    temp_y[i+1] = temp_y[i];
	}
	angle = 0;
	for (int i=10; i<14; i++)
	{
	    temp_x[i] = 
		(int)(10.0*Math.cos(72.0*(double)angle/180.0*Math.PI))+15; 
	    temp_y[i] = 
		(int)(10.0*Math.sin(72.0*(double)angle/180.0*Math.PI))+15; 
	    angle++;
	}
	aro5 = new ImageLinesButton_1_0(14, temp_x, temp_y, aro5Name);

	// simple text buttons
	draw_single_button = new Button("Single Bond");
	draw_double_button = new Button("Double Bond");
	draw_triple_button = new Button("Triple Bond");
	change_atom_type_button = new Button("Atom Type"); 
	lasso_button = new Button("Lasso"); 
	select_button = new Button("Select");
	zoom_button = new Button("Zoom");
	merge_button = new Button("Merge Selected");
	delete_selected_button = new Button("Delete Selected");
	
	// Molecule Editor Label, drawn in Helvetica, bold, 26
	Font font = new Font("Helvetica",Font.BOLD,26);
	global_name = new Label("Molecule Editor",Label.CENTER);
	global_name.setFont(font);
	
	// Version Label, drawn in Helvetica, plain, 12
	font = new Font("Helvetica",Font.PLAIN,12);
	version_name = new Label("1.0",Label.CENTER);
	version_name.setFont(font);

	// atom type label
	atom_type = new Label("Change Selected to:",Label.CENTER);
  
	// Atom type menu
	choice = new Choice();
	choice.addItem("C");
	choice.addItem("B");
	choice.addItem("N");
	choice.addItem("O");
	choice.addItem("F");
	choice.addItem("Na");
	choice.addItem("Mg");
	choice.addItem("Si");
	choice.addItem("P");
	choice.addItem("S");
	choice.addItem("Cl");
	choice.addItem("K");
	choice.addItem("Ca");
	choice.addItem("Mn");
	choice.addItem("Fe");
	choice.addItem("Br");
	choice.addItem("I");
	choice.addItem("?");
  
	// The storage panel for buttons
	// It is stored in a matrix of two columns and 10 rows
	// All the text buttons span 2 rows whereas drawn buttons are in on row
	button_panel = new Panel();
	button_panel.setLayout(gridbag);
	constrain(button_panel, draw_single_button, 0,0,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,0,5);
	constrain(button_panel, draw_double_button, 0,1,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,0,5);
	constrain(button_panel, draw_triple_button, 0,2,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,0,5);
	constrain(button_panel, lasso_button, 0,3,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,0,5);
	constrain(button_panel, select_button, 0,4,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, zoom_button, 0,5,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, delete_selected_button, 0,6,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, merge_button, 0,7,2,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, cyclo6, 0,8,1,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, aro6, 1,8,1,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, cyclo5, 0,9,1,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	constrain(button_panel, aro5, 1,9,1,1,
		    GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER,
		    0.0,0.0, 5,5,5,5);
	
	
	// The rest of the interface
	drawing_area = new Panel();	// The molecule drawing area
	drawing_area.setLayout(new BorderLayout()); 
	aMolecule = new Molecule_1_0(); // Creation of the molecule instance
	drawing_area.add("Center",aMolecule);	// The molecule is put in its
	this.setLayout(gridbag);		// drawing area
	
	message_window.setEditable(false);  // Message window is set not editable
	
	// The interface is set up.  It is a variable width/height grid of
	// 3 rows and 4 columns.  It is used like this:
	//   ___1___||____2____||___3___||____4____||
	// 1|     molecule_editor_label    version
	// 2|    msg_window      at_label    menu
	// 3| buttons       molecule_drawing_area
	// 
	
	constrain(this, global_name, 0,0,3,1,GridBagConstraints.HORIZONTAL, 
	GridBagConstraints.CENTER,1.0,0.0, 5,5,5,5);

	constrain(this, version_name, 3,0,1,1,GridBagConstraints.HORIZONTAL, 
	GridBagConstraints.CENTER,1.0,0.0, 5,5,5,5);

	constrain(this, message_window, 0,1,2,1,GridBagConstraints.NONE, 
	GridBagConstraints.WEST,0.0,0.0, 5,5,5,5);

	constrain(this, atom_type, 2,1,1,1,GridBagConstraints.NONE, 
	GridBagConstraints.EAST,0.0,0.0, 5,5,5,5);

	constrain(this, choice, 3,1,1,1,GridBagConstraints.NONE, 
	GridBagConstraints.WEST,0.0,0.0, 5,5,5,5);

	constrain(this, button_panel, 0,2,1,1,GridBagConstraints.NONE, 
		    GridBagConstraints.NORTHEAST,0.0,0.0, 5,5,5,5);

	constrain(this, drawing_area, 1,2,3,1,GridBagConstraints.BOTH, 
		    GridBagConstraints.CENTER,1.0,1.0, 5,5,5,5);
	show();
    }

    // static function called by the ImageLinesButton_1_0 class instances
    // the clicked on
    public static void buttonMouseUp(String buttonName)
    {
	if (buttonName.compareTo(cyclo6Name) == 0)
	{
	    current_selected = CYCLO6;
	}
	else if (buttonName.compareTo(aro6Name) == 0)
	{
	    current_selected = ARO6;
	}
	else if (buttonName.compareTo(aro5Name) == 0)
	{
	    current_selected = ARO5;
	}
	else if (buttonName.compareTo(cyclo5Name) == 0)
	{
	    current_selected = CYCLO5;
	}
    }
    
    
    // The Event Handling function.  This function acts according to the user
    // inputs.  It calls LocalmouseDown/Up/Move/Drag functions when it detects
    // a mouse event and calls the apropriate functions when a button is clicked.

    public boolean handleEvent(Event e)
    {
	switch (e.id)
	{
		case Event.ACTION_EVENT:
		    if (e.target == draw_single_button)
		    {
			current_selected = SINGLE;
			message_window.setText("Single Bond Drawing");
		    }
		    else if (e.target == draw_double_button)
		    {
			current_selected = DOUBLE;
			message_window.setText("Double Bond Drawing");
		    }
		    else if (e.target == draw_triple_button)
		    {
			current_selected = TRIPLE;
			message_window.setText("Triple Bond Drawing");
		    }
		    else if (e.target == select_button)
		    {
			current_selected = SELECT;
			message_window.setText("Selection Mode");
		    }
		    else if (e.target == lasso_button)
		    {
			current_selected = LASSO;
			message_window.setText("Lasso Mode");
		    }
		    else if (e.target == choice)
		    {
			current_selected = CHANGEAT;
			message_window.setText("Changing Atom Type");
			aMolecule.ChangeAtomTypes((String)e.arg);
			aMolecule.paint(aMolecule.getGraphics());
		    }
		    else if (e.target == zoom_button)
		    {
			current_selected = ZOOM;
			message_window.setText("Zoom Mode");
		    }
		    else if (e.target == delete_selected_button)
		    {
			aMolecule.DeleteSelected(); 
			current_selected = SELECT;
			message_window.setText("Selection Mode");
		    }
		    else if (e.target == merge_button)
		    {
			aMolecule.MergeSelected(); 
			aMolecule.paint(aMolecule.getGraphics());
			current_selected = SELECT;
			message_window.setText("Selection Mode");
		    }
		    else
			current_selected = UNKNOWN;
		    break;
		case Event.MOUSE_DOWN:
			LocalmouseDown(e,e.x,e.y);
			return false;
		case Event.MOUSE_MOVE:
			LocalmouseMove(e,e.x,e.y);
			return false;
		case Event.MOUSE_UP:
			LocalmouseUp(e,e.x,e.y);
			return false;
		case Event.MOUSE_DRAG:
			LocalmouseDrag(e,e.x,e.y);
			return false;
		default: break;
	}
	return true;
    }


    // Function called by handleEvent, that acts when the mouse is dragged
    
    public boolean LocalmouseDrag(Event e, int x, int y)
    {
	Rectangle r = drawing_area.bounds(); // Checks if the mouse is in
	if (x>r.x && xr.y && y -1)
			    {
				aMolecule.redrawAtom(atom_highlighted);
			    }
			    if (id > -1)
			    {
				aMolecule.redrawAtom(id,Color.blue);
			    }
			}
			atom_highlighted = id;

			if (this.x < 0 || this.y < 0)
			    break;
			if (temp_int1 < 0 || temp_int2 < 0)
			{
			    g.setColor(Color.lightGray); 
			    g.drawLine(this.x,this.y,oldx,oldy); 
			    g.setColor(Color.black);
			    break;
			}
			if (oldx > -1)
			{
			    g.setColor(Color.lightGray); 
			    g.drawLine(this.x,this.y,oldx,oldy); 
			    g.setColor(Color.black);
			}
			if (id > -1)
			{
			    if (start_atom > -1 && 
				aMolecule.BondExist(start_atom,id) != -1) 
				oldx = -1;
			    else
			    {
				oldx = aMolecule.atoms_x[id]; 
				oldy = aMolecule.atoms_y[id]; 
				g.drawLine(this.x,this.y, 
					    aMolecule.atoms_x[id],
					    aMolecule.atoms_y[id]);
			    }
			}
			else
			{
			    oldx = temp_int1;
			    oldy = temp_int2; 
			    g.drawLine(this.x,this.y,oldx,oldy);
			}
			break;
		case LASSO:	// add the new point to the lasso and draw the 
				// the latest line
			g.setColor(Color.yellow); 
			g.drawLine(oldx,oldy,temp_int1,temp_int2); 
			lasso_pol.addPoint(temp_int1,temp_int2); 
			oldx = temp_int1;
			oldy = temp_int2; break;
		
		case ZOOM:	// if first lasso drag, find the gravity centre.
				// Otherwise scale the whole thing.
		    if (oldy == -1)
		    {
			oldy = temp_int2;
			gravity_x = 0;
			gravity_y = 0;
			for (int i=0; i= r.width ||
				tempo_int2 <= 0 || tempo_int2 >= r.width)
				break;
			}
			if (runner == aMolecule.nbAtoms)
			{
			    for (runner=0; runner -1 && this.y > -1 &&	    // if mouse outside the 
		 (current_selected == SINGLE ||	    // the drawing area erase
		  current_selected == DOUBLE))	    // the line drawn previously
	{
	    Graphics g = aMolecule.getGraphics(); 
	    g.setColor(Color.lightGray); 
	    g.drawLine(this.x,this.y,oldx,oldy); 
	    g.setColor(Color.black);
	}
	return true;
    }// end mouseDrag


    // Function called by handleEvent, that acts when the mouse is moved
    
    public boolean LocalmouseMove(Event e, int x, int y)
    {
	Rectangle r = drawing_area.bounds();
	if (x>r.x && xr.y && y -1)
		    aMolecule.redrawAtom(atom_highlighted);
		if (id > -1)
		    aMolecule.redrawAtom(id,Color.blue); 
		atom_highlighted = id;
		
		id = aMolecule.cursor_on_bond(temp_int1,temp_int2); 

		if (bond_highlighted != -1)
		    aMolecule.redrawBond(bond_highlighted); 
		if (id != -1) 
		    aMolecule.redrawBond(id,Color.blue);
		bond_highlighted = id;
	    }
	}
	else if (inside_drawing_area)	// if outside drawing area for the first 
	{				// time, redraw molecule
	    inside_drawing_area = false;
	    aMolecule.paint(aMolecule.getGraphics());
	}
	return true;
    }// end mouseMove


    // Function called by handleEvent, that acts when the mouse is clicked
    
    public boolean LocalmouseDown(Event e, int x, int y) 
    {
	Rectangle r = drawing_area.bounds();
	if (x>r.x && xr.y && y -1)	// an atom was selected/deselected
			{
			    this.x = aMolecule.atoms_x[id]; 
			    this.y = aMolecule.atoms_y[id];
			    aMolecule.select_deselect_atom(id);
			    if (aMolecule.atoms_selected[id])
				last_selected = id;
			    else
				last_selected = -1;
			    aMolecule.redrawAtom(id);
			}
			else
			{
			    last_selected = -1;
			    id = aMolecule.cursor_on_bond(temp_int1,temp_int2); 
			    if (id > -1)    // a bond is selected/deselected
			    {
				aMolecule.select_deselect_bond(id); 
				aMolecule.redrawBond(id);
			    }
			}
			break;
		case SINGLE:
		case DOUBLE: 
		case TRIPLE:
			if (id > -1)	// if an atom is clicked on, store the
			{		// coordinates and its id
			    this.x = aMolecule.atoms_x[id]; 
			    this.y = aMolecule.atoms_y[id]; 
			    start_atom = id;
			}
			else
			{
			    this.x = temp_int1;
			    this.y = temp_int2; 
			    start_atom = -1;
			}
			oldx = -1;
			oldy = -1;
			break;
		case LASSO:	// initialise the lasso polygon
			lasso_pol = new Polygon(); 
			lasso_pol.addPoint(temp_int1,temp_int2); 
			oldx = temp_int1;
			oldy = temp_int2; break;
		case ZOOM:  // initialise the zoom
		    oldy = -1;
		    break;
		case CYCLO6:	// redraw the polygon from a possible existing
		case ARO6:	// atom
			    aMolecule.paint(aMolecule.getGraphics());
			    Polygon pol = new Polygon();
			    pol.addPoint(temp_int1, temp_int2);
    			    pol.addPoint(temp_int1, temp_int2+59);
			    pol.addPoint(temp_int1+51, temp_int2+89);
			    pol.addPoint(temp_int1+103, temp_int2+59);
			    pol.addPoint(temp_int1+103, temp_int2);
			    pol.addPoint(temp_int1+51, temp_int2-30);
			    pol.addPoint(temp_int1, temp_int2);
			    aMolecule.getGraphics().drawPolygon(pol);
			    int id2;
			    for (int i=0; i<6; i++)
			    {
				id2 = aMolecule.cursor_on_atom(
					pol.xpoints[i],pol.ypoints[i]);
				if (id2 != -1)
				    aMolecule.redrawAtom(id2,Color.blue);
			    }
			    this.x = temp_int1;
			    this.y = temp_int2;
			    break;
		case CYCLO5:	// redraw the polygon from a possible existing
		case ARO5:	// atom
			    aMolecule.paint(aMolecule.getGraphics());
			    Polygon pol5 = new Polygon();
			    pol5.addPoint(temp_int1, temp_int2);
    			    pol5.addPoint(temp_int1+16, temp_int2+51);
			    pol5.addPoint(temp_int1+71, temp_int2+51);
			    pol5.addPoint(temp_int1+88, temp_int2);
			    pol5.addPoint(temp_int1+44, temp_int2-32);
			    pol5.addPoint(temp_int1, temp_int2);
			    aMolecule.getGraphics().drawPolygon(pol5);
			    int id5;
			    for (int i=0; i<5; i++)
			    {
				id5 = aMolecule.cursor_on_atom(
					pol5.xpoints[i],pol5.ypoints[i]);
				if (id5 != -1)
				    aMolecule.redrawAtom(id5,Color.blue);
			    }
			    this.x = temp_int1;
			    this.y = temp_int2;
			    break;
		default:break;		                
	    }// end switch
	}
	else
	{
	    this.x = -1;
	    this.y = -1;
	}
	return true;
    }


    // Function called by handleEvent, that acts when the mouse button is released
    
    public boolean LocalmouseUp(Event e, int x, int y)
    {
	Rectangle r = drawing_area.bounds();
	int temp_int1 = x - r.x;
	int temp_int2 = y - r.y;
	if (x>r.x && xr.y && y= r.width || 
			    polygon_arrayy[i] >= r.height)
			    must_exit = true;
		    if (must_exit)
		    {
			this.x = -1;
			this.y = -1;
			break;
		    }
		    
		    int indices[] = new int[6];
		    for (int i=0; i<6; i++)
		    {
			indices[i] = aMolecule.cursor_on_atom(
			    polygon_arrayx[i], polygon_arrayy[i]);
			if (indices[i] == -1)
			    indices[i] = aMolecule.AddAtom(
				polygon_arrayx[i], polygon_arrayy[i], 6);
		    }
		    if (current_selected == ARO6)
		    {
			aMolecule.AddBond(indices[0],indices[1],4);
			aMolecule.AddBond(indices[1],indices[2],4);
			aMolecule.AddBond(indices[2],indices[3],4);
			aMolecule.AddBond(indices[3],indices[4],4);
			aMolecule.AddBond(indices[4],indices[5],4);
			aMolecule.AddBond(indices[5],indices[0],4);
		    }
		    else
		    {
			aMolecule.AddBond(indices[0],indices[1],1);
			aMolecule.AddBond(indices[1],indices[2],1);
			aMolecule.AddBond(indices[2],indices[3],1);
			aMolecule.AddBond(indices[3],indices[4],1);
			aMolecule.AddBond(indices[4],indices[5],1);
			aMolecule.AddBond(indices[5],indices[0],1);
		    }
		    aMolecule.paint(aMolecule.getGraphics());
		    this.x = -1;
		    this.y = -1;
		    break;
		case ARO5:	// See comments for 6 membered ring, above.
		case CYCLO5:
		    double scale_fac5 = 1.0+(double)(temp_int2 - this.y)/100.0;
		    
		    int polygon5_arrayx[] = new int[6];
		    int polygon5_arrayy[] = new int[6];
		    polygon5_arrayx[0] = 0;
		    polygon5_arrayy[0] = 0;
		    polygon5_arrayx[1] = (int)(scale_fac5*16.0);
		    polygon5_arrayy[1] = (int)(scale_fac5*51.0);
		    polygon5_arrayx[2] = (int)(scale_fac5*71.0);
		    polygon5_arrayy[2] = (int)(scale_fac5*51.0);
		    polygon5_arrayx[3] = (int)(scale_fac5*88.0);
		    polygon5_arrayy[3] = 0;
		    polygon5_arrayx[4] = (int)(scale_fac5*44.0);
		    polygon5_arrayy[4] = (int)(scale_fac5*(-32.0));
		    polygon5_arrayx[5] = 0;
		    polygon5_arrayy[5] = 0;
		    
		    double theta5 = Math.PI * ((double)(temp_int1-this.x)/100.0);
		    double cost5 = Math.cos(theta5);
		    double sint5 = Math.sin(theta5);
		    int tempx5, tempy5;
		    for (int i=0; i<6; i++)
		    {
			tempx5 = polygon5_arrayx[i];
			tempy5 = polygon5_arrayy[i];
			polygon5_arrayx[i] = (int)((double)tempx5*cost5) - 
			    (int)((double)tempy5*sint5) + this.x;
			polygon5_arrayy[i] = (int)((double)tempx5*sint5) + 
			    (int)((double)tempy5*cost5) + this.y;
		    }
		    for (int i=0; i<6; i++)
		    {
			tempx5 = aMolecule.cursor_on_atom(
			    polygon5_arrayx[i], polygon5_arrayy[i]);
			if (tempx5 != -1)
			{
			    polygon5_arrayx[i] = aMolecule.atoms_x[tempx5];
			    polygon5_arrayy[i] = aMolecule.atoms_y[tempx5];
			    aMolecule.redrawAtom(tempx5,Color.blue);
			}
		    }
		    boolean must_exit5 = false;
		    for (int i=0; i<5; i++)
			if (polygon5_arrayx[i] <= 0 || 
			    polygon5_arrayy[i] <= 0 ||
			    polygon5_arrayx[i] >= r.width || 
			    polygon5_arrayy[i] >= r.height)
			    must_exit5 = true;
		    if (must_exit5)
		    {
			this.x = -1;
			this.y = -1;
			break;
		    }
		    
		    int indices5[] = new int[5];
		    for (int i=0; i<5; i++)
		    {
			indices5[i] = aMolecule.cursor_on_atom(
			    polygon5_arrayx[i], polygon5_arrayy[i]);
			if (indices5[i] == -1)
			    indices5[i] = aMolecule.AddAtom(
				polygon5_arrayx[i], polygon5_arrayy[i], 6);
		    }
		    if (current_selected == ARO5)
		    {
			aMolecule.AddBond(indices5[0],indices5[1],4);
			aMolecule.AddBond(indices5[1],indices5[2],4);
			aMolecule.AddBond(indices5[2],indices5[3],4);
			aMolecule.AddBond(indices5[3],indices5[4],4);
			aMolecule.AddBond(indices5[4],indices5[0],4);
		    }
		    else
		    {
			aMolecule.AddBond(indices5[0],indices5[1],1);
			aMolecule.AddBond(indices5[1],indices5[2],1);
			aMolecule.AddBond(indices5[2],indices5[3],1);
			aMolecule.AddBond(indices5[3],indices5[4],1);
			aMolecule.AddBond(indices5[4],indices5[0],1);
		    }
		    aMolecule.paint(aMolecule.getGraphics());
		    this.x = -1;
		    this.y = -1;
		    break;
		default:break;			        
	    }// end switch
	}
		// If mouse button released outside the drawing area, 
		// cancel ring drawing
	else if ((current_selected == ARO6 || current_selected == CYCLO6 ||
		  current_selected == ARO5 || current_selected == CYCLO5) &&
		 this.x > -1 && this.y > -1)
	{
	    aMolecule.paint(aMolecule.getGraphics());
	    this.x = -1;
	    this.y = -1;
	}
	return true;
    }// End mouseUp


    // This function has been ripped off from David Flanagan's 
    // Java in a Nutshell, O'reilly & associates, Inc.
    // It creates the constraints according to the parameters.
    public void constrain(Container container, Component component,
			    int grid_x, int grid_y,
	    		    int grid_width, int grid_height,
	    		    int fill, int anchor,
			    double weight_x, double weight_y,
			    int top, int left, int bottom, int right)
    {
	GridBagConstraints c = new GridBagConstraints();
	c.gridx = grid_x;
	c.gridy = grid_y; 
	c.gridwidth = grid_width; 
	c.gridheight = grid_height; 
	c.fill = fill;
	c.anchor = anchor; 
	c.weightx = weight_x; 
	c.weighty = weight_y;
	if (top+bottom+left+right > 0)
	    c.insets = new Insets(top,left,bottom,right);
	((GridBagLayout)container.getLayout()).setConstraints(component,c); 
	container.add(component);
    }
    
}// End MoleculeEditor_1_0 class


/*
 * This is the Molecule_1_0 class.  It is used to store and handle all the aspects
 * of a molecule creation, modifications, drawing, etc...
 * Everything is stored in arrays.  The maximum size is 100 atoms and
 * 250 bonds.  It is never checked if these limits are passed!
 */

class Molecule_1_0 extends Canvas{
	private protected static final int MAX_ATOMS = 100;
	private protected static final int MAX_BONDS = 250; 
	protected int atoms_x[];
	protected int atoms_y[];
	protected int nbAtoms;
	protected int nbBonds;
	protected int bonds_1[];
	protected int bonds_2[];
	protected int bonds_type[]; 
	protected int atoms_type[]; 
	protected byte atoms_degree[]; 
	protected boolean atoms_selected[]; 
	protected boolean bonds_selected[]; 
	private protected static final int MAX_RADIUS = 10; 
	protected Polygon bonds_area[];
	protected Font font;
	
    public Molecule_1_0()
    {
	atoms_x = new int [MAX_ATOMS];
	atoms_y = new int [MAX_ATOMS]; 
	atoms_type = new int [MAX_ATOMS]; 
	atoms_degree = new byte [MAX_ATOMS]; 
	bonds_1 = new int [MAX_BONDS]; 
	bonds_2 = new int [MAX_BONDS]; 
	bonds_type = new int [MAX_BONDS]; 
	atoms_selected = new boolean [MAX_ATOMS]; 
	bonds_selected = new boolean [MAX_BONDS]; 
	bonds_area = new Polygon [MAX_BONDS];
	nbAtoms = 0;
	nbBonds = 0;
	font = new Font("Courier",Font.PLAIN,10); 
    }
    
    public int AddAtom(int a, int b, int at)
    {
	atoms_x[nbAtoms] = a;
	atoms_y[nbAtoms] = b; 
	atoms_type[nbAtoms] = at; 
	atoms_degree[nbAtoms] = 0;
	atoms_selected[nbAtoms++] = false;
	return nbAtoms-1;
    }


    public boolean AddBond(int id1, int id2, int bt) 
    {
	if (id1 < nbAtoms && id2 < nbAtoms)
	{
	    int id = BondExist(id1, id2);
	    if (id != -1)
	    {
		if (bonds_type[id] == bt)
		    return false;
		else
		{
		    bonds_type[id] = bt;
		    return true;
		}
	    }
	    bonds_1[nbBonds] = id1;
	    bonds_2[nbBonds] = id2; 
	    bonds_type[nbBonds] = bt;
	    bonds_area[nbBonds] = find_polygon(id1,id2);
	    bonds_selected[nbBonds++] = false;
	    atoms_degree[id1] += 1;
	    atoms_degree[id2] += 1;
	    return true;
	}
	else
	{
	    return false;
	}
    }

    private Polygon find_polygon(int x, int y) 
    {
	Polygon pol = new Polygon();
	int x1 = atoms_x[y] - atoms_x[x]; 
	int y1 = atoms_y[y] - atoms_y[x];
	int x2 = -1 * y1;
	int y2 = x1;
	double norm1 = Math.sqrt((double)(x2*x2+y2*y2));
pol.addPoint(atoms_x[x]+(int)(5.0*(double)x2/norm1)+(int)(8.0*(double)x1/norm1), 
	    atoms_y[x]+(int)(5.0*(double)y2/norm1)+(int)(8.0*(double)y1/norm1));
pol.addPoint(atoms_x[x]+(int)(-5.0*(double)x2/norm1)+(int)(8.0*(double)x1/norm1),
	    atoms_y[x]+(int)(-5.0*(double)y2/norm1)+(int)(8.0*(double)y1/norm1));
pol.addPoint(atoms_x[y]+(int)(-5.0*(double)x2/norm1)+(int)(-8.0*(double)x1/norm1), 
	    atoms_y[y]+(int)(-5.0*(double)y2/norm1)+(int)(-8.0*(double)y1/norm1));
pol.addPoint(atoms_x[y]+(int)(5.0*(double)x2/norm1)+(int)(-8.0*(double)x1/norm1), 
	    atoms_y[y]+(int)(5.0*(double)y2/norm1)+(int)(-8.0*(double)y1/norm1));
	return pol;
    }

    public boolean RemoveBond(int id1, int id2)
    {
	if (id1 < nbAtoms && id2 < nbAtoms)
	{
	    for (int i=0; i=0; i--)
	    if (atoms_selected[i])
		RemoveAtom(i);
	for (int i=nbBonds-1; i>=0; i--)
	    if (bonds_selected[i])
		RemoveBond(i);	
	paint(this.getGraphics());
    }


    int BondExist(int id1, int id2)
    {
	if (id1 != id2 &&
	    id1 < nbAtoms && id2 < nbAtoms)
	{
	    for (int i=0; i 122 || (int)at.charAt(1) < 65)
	    return char2type(at.charAt(0));
	else if (at.length() == 2 || 
		 (int)at.charAt(2) > 122 || (int)at.charAt(2) < 65)
	    return char2type(at.charAt(0), at.charAt(1));
	else 
	    return 200;
    }


    public int cursor_on_atom(int x, int y)
    {
	for (int i=0; i -1)
	    for (int i=0; i= nbBonds)
	    return;
	Graphics g = this.getGraphics();
	g.setColor(c);

	switch (bonds_type[id])
	{
	    case 3:
		x1 = atoms_x[bonds_2[id]] - atoms_x[bonds_1[id]];
		y1 = atoms_y[bonds_2[id]] - atoms_y[bonds_1[id]];
		x2 = -1 * y1;
		y2 = x1;
		norm1 = Math.sqrt((double)(x2*x2+y2*y2));
		
		g.drawLine(
		    atoms_x[bonds_1[id]]+(int)(5.0*(double)x2/norm1), 
		    atoms_y[bonds_1[id]]+(int)(5.0*(double)y2/norm1), 
		    atoms_x[bonds_2[id]]+(int)(5.0*(double)x2/norm1), 
		    atoms_y[bonds_2[id]]+(int)(5.0*(double)y2/norm1));
		g.drawLine(
		    atoms_x[bonds_1[id]]+(int)(-5.0*(double)x2/norm1), 
		    atoms_y[bonds_1[id]]+(int)(-5.0*(double)y2/norm1), 
		    atoms_x[bonds_2[id]]+(int)(-5.0*(double)x2/norm1), 
		    atoms_y[bonds_2[id]]+(int)(-5.0*(double)y2/norm1));
		g.drawLine(atoms_x[bonds_1[id]], atoms_y[bonds_1[id]], 
		    atoms_x[bonds_2[id]], atoms_y[bonds_2[id]]);
		break;
	    case 2:
		x1 = atoms_x[bonds_2[id]] - atoms_x[bonds_1[id]];
		y1 = atoms_y[bonds_2[id]] - atoms_y[bonds_1[id]];
		x2 = -1 * y1;
		y2 = x1;
		norm1 = Math.sqrt((double)(x2*x2+y2*y2));
		
		g.drawLine(
		    atoms_x[bonds_1[id]]+(int)(4.0*(double)x2/norm1), 
		    atoms_y[bonds_1[id]]+(int)(4.0*(double)y2/norm1), 
		    atoms_x[bonds_2[id]]+(int)(4.0*(double)x2/norm1), 
		    atoms_y[bonds_2[id]]+(int)(4.0*(double)y2/norm1));
		g.drawLine(
		    atoms_x[bonds_1[id]]+(int)(-4.0*(double)x2/norm1), 
		    atoms_y[bonds_1[id]]+(int)(-4.0*(double)y2/norm1), 
		    atoms_x[bonds_2[id]]+(int)(-4.0*(double)x2/norm1), 
		    atoms_y[bonds_2[id]]+(int)(-4.0*(double)y2/norm1));
		break;
	    case 4:
		x1 = atoms_x[bonds_2[id]] - atoms_x[bonds_1[id]];
		y1 = atoms_y[bonds_2[id]] - atoms_y[bonds_1[id]];
		norm1 = Math.sqrt((double)(x1*x1+y1*y1));

		g.drawLine(atoms_x[bonds_1[id]], atoms_y[bonds_1[id]], 
		    atoms_x[bonds_1[id]] + (int)((double)x1/5.0), 
		    atoms_y[bonds_1[id]] + (int)((double)y1/5.0));
		
		g.drawLine(
		    atoms_x[bonds_1[id]] + (int)(2.0*(double)x1/5.0), 
		    atoms_y[bonds_1[id]] + (int)(2.0*(double)y1/5.0), 
		    atoms_x[bonds_1[id]] + (int)(3.0*(double)x1/5.0), 
		    atoms_y[bonds_1[id]] + (int)(3.0*(double)y1/5.0));

		g.drawLine(
		    atoms_x[bonds_1[id]] + (int)(4.0*(double)x1/5.0), 
		    atoms_y[bonds_1[id]] + (int)(4.0*(double)y1/5.0), 
		    atoms_x[bonds_2[id]], atoms_y[bonds_2[id]]);
		break;
	    case 1:
	    default:
		g.drawLine(atoms_x[bonds_1[id]], atoms_y[bonds_1[id]], 
		    atoms_x[bonds_2[id]], atoms_y[bonds_2[id]]);
		break;
	}
    }

    public void redrawBond(int id, Color c)
    {
	if (id < 0 || id >= nbBonds)
	    return;
	simpleRedrawBond(id, c);
	if (atoms_selected[bonds_1[id]])
	    redrawAtom(bonds_1[id],Color.red);
	else
	    redrawAtom(bonds_1[id],Color.black);
	if (atoms_selected[bonds_2[id]])
	    redrawAtom(bonds_2[id],Color.red);
	else
	    redrawAtom(bonds_2[id],Color.black);
    }// end redraw_atom


    public void redrawBond(int id)
    {
	if (id > -1 && id < nbBonds)
	{
	    redrawAtom(bonds_1[id]);
	    redrawAtom(bonds_2[id]);
	}
    }


    public void redrawAtom(int id, Color c)
    {
	Graphics g = this.getGraphics();
	g.setColor(Color.lightGray);
	g.fillRect(atoms_x[id]-3,atoms_y[id]-7,10,10);
	g.setColor(c);
	g.drawString(at2string(atoms_type[id]), 
		     atoms_x[id]-2, atoms_y[id]+2);
    }// end redraw_atom


    public void redrawAtom(int id)
    {
	Graphics g = this.getGraphics();
	g.setColor(Color.lightGray);
	if (atoms_type[id] == 6) 
	    g.fillRect(atoms_x[id]-3,atoms_y[id]-7,10,10);
	g.setColor(Color.black);
	if (atoms_degree[id] == 0)
	{
	    if (atoms_selected[id])
		redrawAtom(id,Color.red);
	    else
		redrawAtom(id,Color.black);
	}
	else
	{
	    for (int i=0; i maxx) 
		    maxx = xpoints[i];
		if (xpoints[i] < minx) 
		    minx = xpoints[i];
		if (ypoints[i] > maxy) 
		    maxy = ypoints[i];
		if (ypoints[i] < miny) 
		    miny = ypoints[i];
	    }
	    width = maxx;
	    height = maxy;
	    if (width < 20) 
		width = 20;
	    if (height < 20) 
		height = 20;
	}
    }

    public ImageLinesButton_1_0 (String name) 
    {
	npoints=0;
	width = 20;
	height = 20;
	this.name = new String(name);
    }


    public boolean handleEvent(Event e)
    {
	Graphics g = this.getGraphics();
	switch (e.id)
	{
	    case Event.MOUSE_DOWN:
		reverse_paint(g);
		return false;
	    case Event.MOUSE_UP:
		this.paint(g);
		MoleculeEditor_1_0.buttonMouseUp(new String(this.name)); 
		return false;
	    default: break;
	}
	return true;
    }

    public Dimension preferredSize()
    {
	return new Dimension(width+2*MARGIN,height+2*MARGIN);
    }

    public Dimension minimumSize()
    {
	return new Dimension(width+2*MARGIN,height+2*MARGIN);
    }
	
    public void reverse_paint(Graphics g)
    {
	g.setColor(Color.black); 
	g.fillRoundRect(0,0,width+2*MARGIN,height+2*MARGIN,10,10); 
	g.setColor(Color.white); 
	g.drawRoundRect(0,0,width+2*MARGIN-1,height+2*MARGIN-1,10,10); 
	for (int i=0; i
  
Modified: Thu May 30 16:00:00 1996 GMT
Page accessed 5791 times since Wed Feb 5 07:37:07 2003 GMT