import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.text.*;

/*
 * This is the GUI element that we use when we're doing heterojunctions, and is therefore
 * of great interest. The really interesting stuff appears to be in the makemol() method.
 */

public class P2 extends JPanel implements ActionListener, ItemListener
{
	private JButton B1, B2;
	private JPanel c1, c0, c2, ct1, ct2;
	private JScrollPane ctxt2;
	private JTextArea txlog;
	private JLabel l1, l2, l3, l4, l5, l6;
	private JTextField t1, t2, t3, t4, t5, t6;
	private Mira3D pan3D;
	private JCheckBox cb1, cb2, cb3;
	private GLienzo2 gl;
	private JComboBox combo;
	private boolean bst, atn, hbk;
	public MoleculaT HJ;
	formato fo = new formato (8, "#0.000");
	formato fd = new formato (8, "###0.000");	//para los doble precision
	formato fi = new formato (5, "#####");	//para los enteros

	private final double GAP = 15.0;	//val absoluto de zona de deformacion
	private final double GMI = 2.0;	//val absoluto de zona de no deformacion


	public P2 ()
	{

		HJ = new MoleculaT ();
		pan3D = new Mira3D ();

		setLayout (new BorderLayout ());
		c0 = new JPanel ();
		c0.setLayout (new BorderLayout ());
		c1 = new JPanel ();
		c1.setLayout (new GridLayout (4, 2, 5, 5));
		c2 = new JPanel ();
		c2.setLayout (new GridLayout (5, 1, 5, 5));

		Box ct1 = Box.createHorizontalBox ();
		Box ct2 = Box.createHorizontalBox ();

		  ct1.setBorder (BorderFactory.
				 createCompoundBorder (BorderFactory.
						       createTitledBorder ("1st Tube:\nIndices and length. (\u212B)"), BorderFactory.createEtchedBorder (1)));
		  ct2.setBorder (BorderFactory.
				 createCompoundBorder (BorderFactory.
						       createTitledBorder ("2nd Tube:\nIndices and length. (\u212B)"), BorderFactory.createEtchedBorder (1)));

		  txlog = new JTextArea ("", 6, 15);
		  ctxt2 = new JScrollPane (txlog);
		  txlog.setEditable (false);
		Font fon = new Font ("Courier", 1, 12);
		  txlog.setFont (fon);

		  B1 = new JButton ("CREATE");
		  B1.addActionListener (this);
		  B2 = new JButton ("CREATE RANDOM");
		  B2.addActionListener (this);

		Box cv = Box.createVerticalBox ();
		  cb1 = new JCheckBox ("Ball & Stick (b)");
		  cb1.addItemListener (this);
		  bst = false;
		  cb2 = new JCheckBox ("Atom labels (l)");
		  cb2.addItemListener (this);
		  atn = false;
		  cb3 = new JCheckBox ("Cut back (c)");
		  cb3.addItemListener (this);
		  hbk = false;
		  cv.add (cb1);
		  cv.add (cb2);
		  cv.add (cb3);

		  combo = new JComboBox ();
		  combo.addItem ("No endings");
		  combo.addItem ("Hydrogen");
		  combo.addItem ("Nitrogen");
		//Numeros random
		//int rand1=(int)(25*Math.random());
		//int rand2=(int)(25*Math.random());
		//int rand3=(int)(25*Math.random());
		//int rand4=(int)(25*Math.random());

		  l1 = new JLabel (" i= ");
		  t1 = new JTextField ("5");
		  t1.addActionListener (this);
		  l2 = new JLabel (" j= ");
		  t2 = new JTextField ("5");
		  t2.addActionListener (this);
		  l3 = new JLabel (" l= ");
		  t3 = new JTextField ("20");
		  t3.addActionListener (this);
		  l4 = new JLabel (" i= ");
		  t4 = new JTextField ("10");
		  t4.addActionListener (this);
		  l5 = new JLabel (" j= ");
		  t5 = new JTextField ("10");
		  t5.addActionListener (this);
		  l6 = new JLabel (" l= ");
		  t6 = new JTextField ("20");
		  t6.addActionListener (this);

		  ct1.add (l1);
		  ct1.add (t1);
		  ct1.add (l2);
		  ct1.add (t2);
		  ct1.add (l3);
		  ct1.add (t3);
		  ct2.add (l4);
		  ct2.add (t4);
		  ct2.add (l5);
		  ct2.add (t5);
		  ct2.add (l6);
		  ct2.add (t6);



		  gl = new GLienzo2 ();
		  gl.redraw (Integer.parseInt (t1.getText ().trim ()),
			     Integer.parseInt (t2.getText ().trim ()),
			     Integer.parseInt (t4.getText ().trim ()), Integer.parseInt (t5.getText ().trim ()), true);
		  gl.setPreferredSize (new Dimension (150, 150));

		Box b2 = Box.createVerticalBox ();
		  b2.setPreferredSize (new Dimension (220, 200));
		  b2.add (ct1);
		  b2.add (ct2);
		  b2.add (combo);
		  b2.add (B1);
		//b2.add(B2);

		  c2.setMaximumSize (new Dimension (300, 300));

		  c0.add (b2, BorderLayout.NORTH);
		//c0.add(gl, BorderLayout.CENTER );
		  c0.add (cv, BorderLayout.SOUTH);
		  add (c0, BorderLayout.WEST);
		  add (ctxt2, BorderLayout.SOUTH);
		  add (pan3D, BorderLayout.CENTER);
		  ctxt2.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		  ctxt2.setBorder (BorderFactory.
				   createCompoundBorder (BorderFactory.createTitledBorder ("Generation Log."), BorderFactory.createEtchedBorder (1)));
		  HJ = new MoleculaT ();
	}

	public void actionPerformed (ActionEvent ev)
	{
		String etiq = ev.getActionCommand ();

		int i1, i2, j1, j2;
		if (etiq == "CREATE") {

			try {
				i1 = Integer.parseInt (t1.getText ().trim ());
				j1 = Integer.parseInt (t2.getText ().trim ());
				i2 = Integer.parseInt (t4.getText ().trim ());
				j2 = Integer.parseInt (t5.getText ().trim ());
			}

			catch (Exception e) {
				JOptionPane.showMessageDialog (null, "Stop: wrong input", "Stop", 0);
				return;
			}
		} else if (etiq == "CREATE RANDOM") {
			i1 = (int) (25 * Math.random ());
			t1.setText ("" + i1);
			j1 = (int) (25 * Math.random ());
			t2.setText ("" + j1);
			i2 = (int) (25 * Math.random ());
			t4.setText ("" + i2);
			j2 = (int) (25 * Math.random ());
			t5.setText ("" + j2);
		} else {
			i1 = 2;
			j1 = 2;
			i2 = 4;
			j2 = 4;
		}
		gl.redraw (i1, j1, i2, j2, true);

		makemol ();
	}

	public void itemStateChanged (ItemEvent e)
	{
		Object source = e.getItemSelectable ();
		if (source == cb1) {
			if (e.getStateChange () == ItemEvent.DESELECTED)
				bst = false;
			else
				bst = true;
			pan3D.repinta (bst, atn, hbk);
		} else if (source == cb2) {
			if (e.getStateChange () == ItemEvent.DESELECTED)
				atn = false;
			else
				atn = true;
			pan3D.repinta (bst, atn, hbk);
		} else if (source == cb3) {
			if (e.getStateChange () == ItemEvent.DESELECTED)
				hbk = false;
			else
				hbk = true;
			pan3D.repinta (bst, atn, hbk);
		}


	}



	public void makemol ()
	{

		boolean ejes = false;
		boolean vecs = false;
		boolean despleg = false;

		Nanotubo NTA, NTB;	//ambos tubos
		String td1, td2;	//dos cadenas etiquetan primer y segundo defecto
		HJ.vaciar ();


		int n = 0, m = 0;	//indices de la cinta strip

		int i1 = 0;
		int i2 = 0;
		int j1 = 0;
		int j2 = 0;
		double lent1 = 0;
		double lent2 = 0;

		// Get parameters from the GUI.
		try {
			i1 = Integer.parseInt (t1.getText ().trim ());	//pillamos indice se tubos
			j1 = Integer.parseInt (t2.getText ().trim ());
			i2 = Integer.parseInt (t4.getText ().trim ());
			j2 = Integer.parseInt (t5.getText ().trim ());
			lent1 = Double.parseDouble (t3.getText ().trim ());
			lent2 = Double.parseDouble (t6.getText ().trim ());
		}

		catch (NumberFormatException e) {
			JOptionPane.showMessageDialog (null, "Stop: wrong input", "Stop", 0);
			return;
		}


		HJ.setInfo ("(" + i1 + "," + j1 + ")-(" + i2 + "," + j2 + ") Nanotube Heterojunction with lengths " + lent1 + " and " + lent2 + " A.");
		txlog.setText
			("---------------------------------------------------------\n"
			 + " GENERATION OF A (" + i1 + "," + j1 + ")-(" + i2 +
			 "," + j2 + ") CARBON NANOTUBE JUNCTION\n" + "---------------------------------------------------------\n");


		if (i1 == 0 && j1 == 0) {
			JOptionPane.showMessageDialog (null, "Error: 1st tube's indices are incorrect", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		if (i2 == 0 && j2 == 0) {
			JOptionPane.showMessageDialog (null, "Error: 2nd tube's indices are incorrect", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}



		// If the user put in any negative parameters, fix that now
		boolean cambio1 = false;  // cambio --> change
		boolean cambio2 = false;
		for (; (i1 < 0) || (j1 < 0);) {
			int i1n = -j1;
			int j1n = i1 + j1;
			i1 = i1n;
			j1 = j1n;
			cambio1 = true;
		}
		for (; (i2 < 0) || (j2 < 0);) {
			int i2n = -j2;
			int j2n = i2 + j2;
			i2 = i2n;
			j2 = j2n;
			cambio2 = true;
		}

		if (cambio1) {
			JOptionPane.showMessageDialog (null,
						       "Warning: Indices of 1st tube automatically translated to ("
						       + i1 + "," + j1 + ")", "Warning", JOptionPane.INFORMATION_MESSAGE);
			t1.setText ("" + i1);
			t2.setText ("" + j1);
		}
		if (cambio2) {
			JOptionPane.showMessageDialog (null,
						       "Warning: Indices of 2nd tube automatically translated to ("
						       + i2 + "," + j2 + ")", "Warning", JOptionPane.INFORMATION_MESSAGE);
			t4.setText ("" + i2);
			t5.setText ("" + j2);
		}


		// Check validity conditions on parameters
		if (i2 == i1 && j2 == j1) {
			JOptionPane.showMessageDialog (null, "Error: Indices of both tubes coincide", "Error", JOptionPane.ERROR_MESSAGE);
			return;
		}
		if (i2 < 2 && j2 < 2) {
			JOptionPane.showMessageDialog (null, "Stop: 2nd tube too narrow", "Stop", 0);
			return;
		}
		if (i1 < 2 && j1 < 2) {
			JOptionPane.showMessageDialog (null, "Stop: 1st tube too narrow", "Stop", 0);
			return;
		}



		boolean creciente = true;
		int c = 0;

		// Create the two nanotubes to be joined
		NTA = new Nanotubo (i1, j1, 2.46);
		NTB = new Nanotubo (i2, j2, 2.46);

		int guess = naproxatomos (NTA.radio (), lent1, NTB.radio (),
					  lent2);

		// Warn if structure is large enough to be potentially problematic
		if (guess > 6000) {
			JOptionPane.showMessageDialog (null, "Structure will have more than 6000 Atoms. Process stopped.", "STOP", 0);
			return;
		} else if (guess > 4000) {
			int sale = JOptionPane.showConfirmDialog (null,
								  "Structure will have more than 4000 Atoms. Continue at your own risk",
								  "Warning", 0);
			if (sale == 1)
				return;
		} else if (guess > 2000) {
			JOptionPane.showMessageDialog (null, "Large Structure: using low-consumption display.", "Big molecule", 1);
			pan3D.setlrd ();
		} else
			pan3D.unsetlrd ();

		// The actual algorithm starts here
		int nad = j2 - j1;
		int mad = i1 - i2 + j1 - j2;
		int nbd = i1 + j1 - j2;
		int mbd = i2 - i1 + j2;


		int nai = i1 - i2 + j1 - j2;
		int mai = i2 - i1;
		int nbi = i2 - j1 + j2;
		int mbi = j1 - i2 + i1;

		if (NTA.radio () < NTB.radio ()) {
			creciente = true;
			c = -1;
			n = nai;
			m = mai;
			td1 = "heptagon";
			td2 = "pentagon";
		} else {
			creciente = false;
			c = 1;
			n = nad;
			m = mad;
			td1 = "pentagon";
			td2 = "heptagon";
		}


		txlog.setText (txlog.getText () + "\nThe " + td1 + " comes first, and the " + td2 + " is at (" + n + "," + m + "). ");

		if (!creciente)
			txlog.setText (txlog.getText () + "The strip is (" + nad + "," + mad + ")[1,0];(" + nbd + "," + mbd + ")[-1,0];");
		else
			txlog.setText (txlog.getText () + "The strip is (" + nai + "," + mai + ")[1,0];(" + nbi + "," + mbi + ")[-1,0];");




		MoleculaB conoplano = new MoleculaB ();


		double A = 2.46;
		double r3 = Math.sqrt (3);



		pto2D pA = new pto2D (0.0, 0.0);
		pto2D pB = new pto2D (A * (i1 + j1 * 0.5), A * r3 / 2 * j1);
		pto2D pC = new pto2D (A * (n + m * 0.5), A * r3 / 2 * m);
		pto2D pD = new pto2D (A * (n + i2 + (m + j2) * 0.5),
				      A * r3 / 2 * (m + j2));


		// detectanconc --> they detect conc?
		int nconc = detectanconc (pB, pA, pC, pD);

		if (nconc == 0 || nconc == 1) {
			txlog.setText (txlog.getText () + "\n\nHeterojunction requires a cone");
			txlog.setText (txlog.getText () + ". Starting cone determination:");

//DETECTAMOS EL RETCTANGULO que contiene el trapezoide magico
//WE DETECTED the RECTANGLE that contains the magical trapezoid 
			double maxcx = Math.max (Math.max (pA.x, pB.x),
						 Math.max (pC.x, pD.x));
			double maxcy = Math.max (Math.max (pA.y, pB.y),
						 Math.max (pC.y, pD.y));
			double mincx = Math.min (Math.min (pA.x, pB.x),
						 Math.min (pC.x, pD.x));
			double mincy = Math.min (Math.min (pA.y, pB.y),
						 Math.min (pC.y, pD.y));
			if (despleg) {
				HJ.addVert (pA, 8, Color.black);
				HJ.addVert (pB, 8, Color.black);
				HJ.addVert (pC, 8, Color.black);
				HJ.addVert (pD, 8, Color.black);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 2);
				HJ.conecta (HJ.nvert () - 2, HJ.nvert () - 4);
				HJ.conecta (HJ.nvert () - 4, HJ.nvert () - 3);
				HJ.conecta (HJ.nvert () - 3, HJ.nvert () - 1);
			}

			double sty = (int) ((mincy - 2.0 * A * r3) / (A * r3)) * A * r3;
			double stx = (int) ((mincx - 2.0 * A) / (A)) * A;


			for (double cely = sty; cely <= (maxcy - mincy) + 4 * A * r3; cely = cely + A * r3)
				for (double celx = stx; celx <= (maxcx - mincx) + 4 * A; celx = celx + A) {
					pto2D at1 = new pto2D (celx,
							       cely + A / r3);
					pto2D at2 = new pto2D (celx,
							       cely - A / r3);
					pto2D at3 = new pto2D (celx + A / 2,
							       cely + A / 2 / r3);
					pto2D at4 = new pto2D (celx + A / 2,
							       cely - A / 2 / r3);
					if (at1.dentro4l (pB, pA, pC, pD)) {
						conoplano.addVert (at1, 6);
						if (despleg)
							HJ.addVert (at1, 6, Color.orange);
					}	//
					if (at2.dentro4l (pB, pA, pC, pD)) {
						conoplano.addVert (at2, 6);
						if (despleg)
							HJ.addVert (at2, 6, Color.orange);
					}	//
					if (at3.dentro4l (pB, pA, pC, pD)) {
						conoplano.addVert (at3, 6);
						if (despleg)
							HJ.addVert (at3, 6, Color.orange);
					}	//
					if (at4.dentro4l (pB, pA, pC, pD)) {
						conoplano.addVert (at4, 6);
						if (despleg)
							HJ.addVert (at4, 6, Color.orange);
					}	//
				}

			pto2D pV = null;

			double fc = 0;
			if (creciente) {
				pV = new pto2D (A * (i1 + j1 - i1 * 0.5), -A * r3 / 2.0 * i1);
				fc = -1;
			} else {
				pV = new pto2D (A * (-j1 + (i1 + j1) * 0.5), A * r3 / 2.0 * (i1 + j1));
				fc = 1;
			}

			double angucono = Math.asin (0.5 / Math.PI);
			double db = pV.dista (pA);

			for (int i = 0; i < conoplano.nvert (); i++) {
				pto3D ptp = conoplano.vert (i);
				double di = pV.dista (ptp);
				double an;
				if (creciente)
					an = pA.menos (pV).a2D ().angulocwhasta (ptp.menos (pV).a2D ());
				else
					an = pA.menos (pV).a2D ().anguloccwhasta (ptp.menos (pV).a2D ());

				double TX = di * Math.sin (angucono) * Math.cos (6.0 * an);
				double TY = di * Math.sin (angucono) * Math.sin (6.0 * an);
				double TZ = fc * (db - di) * Math.cos (angucono);

				pto3D ptemp = new pto3D (TX, TY, TZ);
				if (!despleg)
					if (!HJ.ocupa1 (ptemp))
						HJ.addVert (ptemp, 6);

			}
			txlog.setText (txlog.getText () + "\nThe cone is completed with " + HJ.nvert () + " atoms");


			if (lent1 <= 15. || lent2 <= 15.)
				txlog.setText (txlog.getText () + "\n\nWarning: Open ends remain deformed: Increase length up to 15 Angstrom.");

			double curvaP = 0.22;
			double curvaH = 0.22;

			pto3D lt1 = new pto3D ();
			pto3D lt2 = new pto3D ();

			txlog.setText (txlog.getText () + "\n\nDetermination of both tubes. ");


			double anT2;
			if (creciente)
				anT2 = pA.menos (pV).a2D ().angulocwhasta (pC.menos (pV).a2D ());
			else
				anT2 = pA.menos (pV).a2D ().anguloccwhasta (pC.menos (pV).a2D ());
			double dhT2 = anT2 * 6.0;
			txlog.setText (txlog.getText () + " Tubes 1 and 2 form a dihedral angle of " + (int) (dhT2 * 180. / Math.PI) + " deg.");
//y creamos los vec --> and we create the vector
			if (!creciente) {
				lt1 = new pto3D (Math.sin (curvaP), 0, Math.cos (curvaP));
				lt2 = new pto3D (Math.sin (curvaH) * Math.cos (dhT2), Math.sin (curvaH) * Math.sin (dhT2), Math.cos (curvaH));
			} else {
				lt1 = new pto3D (-Math.sin (curvaH), 0, Math.cos (curvaH));
				lt2 = new pto3D (-Math.sin (curvaP) * Math.cos (dhT2), -Math.sin (curvaP) * Math.sin (dhT2), Math.cos (curvaP));
			}

			double ratio1x = lt1.x / lt1.z;
			double ratio1y = lt1.y / lt1.z;
			double ratio2x = lt2.x / lt2.z;
			double ratio2y = lt2.y / lt2.z;

			txlog.setText (txlog.getText () + "\n\nStarting 1st tube construction. ");

			pto2D pAcm1 = new pto2D (0.0, 0.0);
			pto2D pBcm1 = new pto2D (A * (i1 + j1 * 0.5),
						 A * r3 / 2 * j1);
//determinamos el vector transpuesto --> we determine the transposed vector
			pto2D pTcm1 = new pto2D (pBcm1.y, -pBcm1.x);
			pto3D pTcm1corto = pTcm1.aversor ().escala (lent1);
			pto2D pCcm1 = pAcm1.mas (pTcm1corto).a2D ();
			pto2D pDcm1 = pBcm1.mas (pTcm1corto).a2D ();

			txlog.setText (txlog.getText () + "Filling 1st tube planar projection ");

			double maxcx1 = Math.max (Math.max (pAcm1.x, pBcm1.x),
						  Math.max (pCcm1.x, pDcm1.x));
			double maxcy1 = Math.max (Math.max (pAcm1.y, pBcm1.y),
						  Math.max (pCcm1.y, pDcm1.y));
			double mincx1 = Math.min (Math.min (pAcm1.x, pBcm1.x),
						  Math.min (pCcm1.x, pDcm1.x));
			double mincy1 = Math.min (Math.min (pAcm1.y, pBcm1.y),
						  Math.min (pCcm1.y, pDcm1.y));

			MoleculaB cachotubo1 = new MoleculaB ();
			int sty1 = (int) Math.round ((mincy1 - 3.0) / A / r3);
			int stx1 = (int) Math.round ((mincx1 - 3.0) / A);
			int enx1 = (int) Math.round ((maxcx1 + 3.0) / A);
			int eny1 = (int) Math.round ((maxcy1 + 3.0) / A / r3);

			int nummax1 = (int) (6 * (maxcx1 - mincx1) * (maxcy1 - mincy1) / A / A / r3);
			int[][] posis1 = new int[3][nummax1];
			int indi1 = 0;

			for (int cely = sty1; cely <= eny1; cely++)	//recorrido vertical --> vertical route
				for (int celx = stx1; celx <= enx1; celx++)	//recorrido horizontal
				{
					pto2D at1 = new pto2D (celx * A,
							       cely * A * r3 + A / r3);
					pto2D at2 = new pto2D (celx * A,
							       cely * A * r3 - A / r3);
					pto2D at3 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 + A / 2.0 / r3);
					pto2D at4 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 - A / 2.0 / r3);
					if (at1.dentro4l (pAcm1, pBcm1, pDcm1, pCcm1)) {
						cachotubo1.addVert (at1, 6);
						posis1[0][indi1] = 1;
						posis1[1][indi1] = celx - cely;
						posis1[2][indi1] = cely * 2;
						indi1++;
						if (despleg)
							HJ.addVert (at1, 6, Color.blue);
					}	//
					if (at2.dentro4l (pAcm1, pBcm1, pDcm1, pCcm1)) {
						cachotubo1.addVert (at2, 6);
						posis1[0][indi1] = 2;
						posis1[1][indi1] = celx - cely + 1;
						posis1[2][indi1] = cely * 2 - 2;
						indi1++;
						if (despleg)
							HJ.addVert (at2, 6, Color.blue);
					}	//
					if (at3.dentro4l (pAcm1, pBcm1, pDcm1, pCcm1)) {
						cachotubo1.addVert (at3, 6);
						posis1[0][indi1] = 2;
						posis1[1][indi1] = celx - cely + 1;
						posis1[2][indi1] = cely * 2 - 1;
						indi1++;
						if (despleg)
							HJ.addVert (at3, 6, Color.blue);
					}	//
					if (at4.dentro4l (pAcm1, pBcm1, pDcm1, pCcm1)) {
						cachotubo1.addVert (at4, 6);
						posis1[0][indi1] = 1;
						posis1[1][indi1] = celx - cely + 1;
						posis1[2][indi1] = cely * 2 - 1;
						indi1++;
						if (despleg)
							HJ.addVert (at4, 6, Color.blue);
					}	//
				}

			txlog.setText (txlog.getText () + "with " + cachotubo1.nvert () + " atoms.");


			double dihedroT1 = 0;

			double da1 = pV.dista (pA);
			pto3D pAm1 = pA.ptomediocon (pB);
			double dop1 = pV.dista (pAm1);
			// proyectado --> projected
			pto3D pdefproy1 = new pto3D (da1 * Math.sin (angucono), 0, fc * (db - da1) * Math.cos (angucono));	//P proyectado 
			pto3D odefproy1 = new pto3D (-dop1 * Math.sin (angucono), 0, fc * (db - dop1) * Math.cos (angucono));	//P proyectado
			pto3D pstart1 = pdefproy1.ptomediocon (odefproy1);
			pto3D pori1 = pdefproy1.menos (pstart1);

			if (ejes) {
				HJ.addVert (pdefproy1, 9);
				HJ.addVert (odefproy1, 8);
				HJ.addVert (pstart1.mas (lt1.escala (-20)), 7);
				HJ.addVert (pstart1, 6);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 2);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 3);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 4);
			}

			for (int i = 0; i < cachotubo1.nvert (); i++) {
				pto3D ptp = cachotubo1.vert (i);
				pto2D puntoproy = ptp.proyeccplano (pTcm1corto).a2D ();
				double diz = ptp.proyeccplano (pBcm1).modulo ();

				double di = pV.dista (puntoproy);
				double an;
				if (creciente) {
					an = pA.menos (pV).a2D ().angulocwhasta (puntoproy.menos (pV).a2D ());
				} else {
					an = pA.menos (pV).a2D ().anguloccwhasta (puntoproy.menos (pV).a2D ());
				}

				double TX = di * Math.sin (angucono) * Math.cos (an * 6.0) - ratio1x * diz;
				// pero para el ajuste del primer tubo, es negativo
				// but for the adjustment of the first tube, it is negative
				double TY = di * Math.sin (angucono) * Math.sin (an * 6.0) - ratio1y * diz;

				double TZ = fc * (db - di) * Math.cos (angucono) - diz;

				pto3D ptemp = new pto3D (TX, TY, TZ);

				int tipoat = posis1[0][i];
				int celdai = posis1[1][i];
				int celdaj = posis1[2][i];
				double co = 0.0;

				if (diz < GMI)
					co = 0.0;
				else if (diz > GAP)
					co = 1.0;
				else
					co = (diz - GMI) / (GAP - GMI);



				pto3D pperf = aproxNT (ptemp,	//Punto a aproximar
						       tipoat,
						       celdai, celdaj,
						       pstart1,
						       i1, j1,
						       lt1,
						       pori1,
						       co);


				if (!despleg)
					if (!HJ.ocupa1 (pperf))
						HJ.addVert (pperf, 6, Color.gray);



			}

//TEST
			if (vecs) {
				pto3D pbaseO = aproxNT (new pto3D (0, 0, 0), 1, 0, 0,
							pstart1, i1, j1, lt1,
							new pto3D (1, 0, 0), 1);
				pto3D pbase1 = aproxNT (new pto3D (0, 0, 0), 1, 1, 0,
							pstart1, i1, j1, lt1,
							new pto3D (1, 0, 0), 1);
				pto3D pbase2 = aproxNT (new pto3D (0, 0, 0), 1, 0, 1,
							pstart1, i1, j1, lt1,
							new pto3D (1, 0, 0), 1);
				HJ.addVert (pbaseO, 6);
				HJ.addVert (pbase1, 7);
				HJ.addVert (pbase2, 8);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 3);
				HJ.conecta (HJ.nvert () - 2, HJ.nvert () - 3);
			}


			txlog.setText (txlog.getText () + "\nStarting 2nd tube construction. ");

			pto2D pAcm2 = pC.clona ().a2D ();
			pto2D pBcm2 = pD.clona ().a2D ();

			pto2D pTcm2 = new pto2D (-(pBcm2.y - pAcm2.y),
						 pBcm2.x - pAcm2.x);
			pto3D pTcm2corto = pTcm2.aversor ().escala (lent2);
			pto2D pCcm2 = pAcm2.mas (pTcm2corto).a2D ();
			pto2D pDcm2 = pBcm2.mas (pTcm2corto).a2D ();

			double maxcx2 = Math.max (Math.max (pAcm2.x, pBcm2.x),
						  Math.max (pCcm2.x, pDcm2.x));
			double maxcy2 = Math.max (Math.max (pAcm2.y, pBcm2.y),
						  Math.max (pCcm2.y, pDcm2.y));
			double mincx2 = Math.min (Math.min (pAcm2.x, pBcm2.x),
						  Math.min (pCcm2.x, pDcm2.x));
			double mincy2 = Math.min (Math.min (pAcm2.y, pBcm2.y),
						  Math.min (pCcm2.y, pDcm2.y));


			txlog.setText (txlog.getText () + "Filling 2nd tube planar projection ");

			MoleculaB cachotubo2 = new MoleculaB ();


			int sty2 = (int) Math.round ((mincy2 - 3.0) / A / r3);
			int stx2 = (int) Math.round ((mincx2 - 3.0) / A);
			int enx2 = (int) Math.round ((maxcx2 + 3.0) / A);
			int eny2 = (int) Math.round ((maxcy2 + 3.0) / A / r3);

			int nummax2 = (int) (6 * (maxcx2 - mincx2) * (maxcy2 - mincy2) / A / A / r3);
			int[][] posis2 = new int[3][nummax2];
			int indi2 = 0;

			for (int cely = sty2; cely <= eny2; cely++)	//recorrido vertical, con
				for (int celx = stx2; celx <= enx2; celx++)	//recorrido horizontal
				{
					pto2D at1 = new pto2D (celx * A,
							       cely * A * r3 + A / r3);
					pto2D at2 = new pto2D (celx * A,
							       cely * A * r3 - A / r3);
					pto2D at3 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 + A / 2.0 / r3);
					pto2D at4 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 - A / 2.0 / r3);
					if (at1.dentro4l (pBcm2, pAcm2, pCcm2, pDcm2)) {
						cachotubo2.addVert (at1, 6);
						posis2[0][indi2] = 1;
						posis2[1][indi2] = celx - cely;
						posis2[2][indi2] = cely * 2;
						indi2++;
						if (despleg)
							HJ.addVert (at1, 6, Color.red);
					}	//
					if (at2.dentro4l (pBcm2, pAcm2, pCcm2, pDcm2)) {
						cachotubo2.addVert (at2, 6);
						posis2[0][indi2] = 2;
						posis2[1][indi2] = celx - cely + 1;
						posis2[2][indi2] = cely * 2 - 2;
						indi2++;
						if (despleg)
							HJ.addVert (at2, 6, Color.red);
					}	//
					if (at3.dentro4l (pBcm2, pAcm2, pCcm2, pDcm2)) {
						cachotubo2.addVert (at3, 6);
						posis2[0][indi2] = 2;
						posis2[1][indi2] = celx - cely + 1;
						posis2[2][indi2] = cely * 2 - 1;
						indi2++;
						if (despleg)
							HJ.addVert (at3, 6, Color.red);
					}	//
					if (at4.dentro4l (pBcm2, pAcm2, pCcm2, pDcm2)) {
						cachotubo2.addVert (at4, 6);
						posis2[0][indi2] = 1;
						posis2[1][indi2] = celx - cely + 1;
						posis2[2][indi2] = cely * 2 - 1;
						indi2++;
						if (despleg)
							HJ.addVert (at4, 6, Color.red);
					}	//
				}


			txlog.setText (txlog.getText () + "with " + cachotubo2.nvert () + " atoms.");

			double dc2 = pV.dista (pC);
			pto3D pCm2 = pC.ptomediocon (pD);
			double dop2 = pV.dista (pCm2);
			pto3D pdefproy2 = new pto3D (dc2 * Math.sin (angucono) * Math.cos (dhT2),
						     dc2 * Math.sin (angucono) * Math.sin (dhT2),
						     fc * (db - dc2) * Math.cos (angucono));	//P proyectado 
			pto3D odefproy2 = new pto3D (-dop2 * Math.sin (angucono) * Math.cos (dhT2)
						     ,
						     -dop2 * Math.sin (angucono) * Math.sin (dhT2)
						     , fc * (db - dop2) * Math.cos (angucono));	//P proyectado 

			pto3D pstart2 = pdefproy2.ptomediocon (odefproy2);

			pto3D pori2 = pdefproy2.menos (pstart2);

			if (ejes) {
				HJ.addVert (pdefproy2, 9);
				HJ.addVert (odefproy2, 8);
				HJ.addVert (pstart2.mas (lt2.escala (20)), 7);
				HJ.addVert (pstart2, 6);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 2);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 3);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 4);
			}
//////////////////////////////

			for (int i = 0; i < cachotubo2.nvert (); i++) {
				pto3D ptp = cachotubo2.vert (i).menos (pAcm2);	//ha de ser relativo 
				pto2D puntoproy = ptp.proyeccplano (pTcm2corto).mas (pAcm2).a2D ();	//ha de ser respecto al origen
				double diz = ptp.proyeccplano (pBcm2.menos (pAcm2)).modulo ();	//distancia al vector quiral 2, 
				double di = pV.dista (puntoproy);

				double an;
				if (creciente) {
					an = pA.menos (pV).a2D ().angulocwhasta (puntoproy.menos (pV).a2D ());
				} else {
					an = pA.menos (pV).a2D ().anguloccwhasta (puntoproy.menos (pV).a2D ());
				}

				double TX = di * Math.sin (angucono) * Math.cos (6.0 * an) + diz * ratio2x;	//diz ahora tiene que ser ositivo
				double TY = di * Math.sin (angucono) * Math.sin (6.0 * an) + diz * ratio2y;
				double TZ = fc * (db - di) * Math.cos (angucono) + diz;	// y aqui tambien positivo, por eso hay que restarla

				pto3D ptemp = new pto3D (TX, TY, TZ);

				int tipoat = posis2[0][i];
				int celdai = posis2[1][i] - n;
				int celdaj = posis2[2][i] - m;
				double co = 0.0;

				if (diz < GMI)
					co = 0.0;
				else if (diz > GAP)
					co = 1.0;	//ES el Gap de AProximacion;
				else
					co = (-diz + GMI) / (-GAP + GMI);


				pto3D pperf = aproxNT (ptemp,	//Punto a aproximar
						       tipoat,
						       celdai, celdaj,
						       pstart2,
						       i2, j2,
						       lt2,
						       pori2,
						       co);

				if (!despleg)
					if (!HJ.ocupa1 (pperf))
						HJ.addVert (pperf, 6, Color.gray);


			}

			if (vecs) {
				pto3D pbaseO2 = aproxNT (new pto3D (0, 0, 0), 1, 0, 0,
							 pstart2, i2, j2, lt2, pori2,
							 1);
				pto3D pbase12 = aproxNT (new pto3D (0, 0, 0), 1, 1, 0,
							 pstart2, i2, j2, lt2, pori2,
							 1);
				pto3D pbase22 = aproxNT (new pto3D (0, 0, 0), 1, 0, 1,
							 pstart2, i2, j2, lt2, pori2,
							 1);
				HJ.addVert (pbaseO2, 6);
				HJ.addVert (pbase12, 7);
				HJ.addVert (pbase22, 8);
				HJ.conecta (HJ.nvert () - 1, HJ.nvert () - 3);
				HJ.conecta (HJ.nvert () - 2, HJ.nvert () - 3);
			}
//represtentacion vec base




			txlog.setText (txlog.getText () + "\n\nStructure (" +
				       i1 + "," + j1 + ")-(" + i2 + "," + j2 + ") completed with " + HJ.nvert () + " atoms");
		} else {

			txlog.setText (txlog.getText () + "\nHeterojunction cannot be formed with a cone. Using alternative algorithm.");


			pto2D Qad = new pto2D (A * (nad + mad * 0.5),
					       A * r3 / 2 * mad);
			pto2D Qai = new pto2D (A * (nai + mai * 0.5),
					       A * r3 / 2 * mai);

			pto2D pA1 = new pto2D (0, 0);
			pto2D pB1 = new pto2D (A * (i1 + j1 * 0.5),
					       A * r3 / 2 * j1);

			double angulod = pB1.angulocong (Qad);
			double anguloi = pB1.angulocong (Qai);

			boolean directo = true;

			if ((angulod < 90.) && (anguloi > 90.)) {
				directo = true;
				n = nad;
				m = mad;
			} else if ((anguloi < 90.) && (angulod > 90.)) {
				directo = false;
				n = nai;
				m = mai;
			} else
				JOptionPane.showMessageDialog (null, "Warning: Unexpected geometry!!", "Warning", JOptionPane.INFORMATION_MESSAGE);

			pto2D pC1 = new pto2D (A * (n + m * 0.5), A * r3 / 2 * m);

			pto2D pA2 = new pto2D (0, 0);
			pto2D pB2 = new pto2D (A * (i2 + j2 * 0.5),
					       A * r3 / 2 * j2);

			int nprima = 0;
			int mprima = 0;
			if (directo) {
				nprima = -m;
				mprima = n + m;
			} else {
				nprima = n + m;
				mprima = -n;
			}

			pto2D pC2 = new pto2D (A * (nprima + mprima * 0.5),
					       A * r3 / 2 * mprima);

			double lonmint1 = pC1.proyeccplano (pB1).modulo ();
			double lonmint2 = pC2.proyeccplano (pB2).modulo ();

			double lm = Math.max (lonmint1, lonmint2);

			if (lent1 <= lonmint1) {
				txlog.setText (txlog.getText () +
					       "\n\nWarning: 1st tube's length too short: Augmented automatically to " + (int) (lonmint1 + 1) + " Angstrom.");
				t3.setText ("" + (int) (lonmint1 + 1));
			}
			if (lent2 <= lonmint2) {
				txlog.setText (txlog.getText () +
					       "\n\nWarning: 2nd tube's length too short: Augmented automatically to " + (int) (lonmint2 + 1) + " Angstrom.");
				t6.setText ("" + (int) (lonmint2 + 1));
			}
			if (lent1 <= 15. || lent2 <= 15.)
				txlog.setText (txlog.getText () + "\n\nWarning: Open ends remain deformed: Increase length up to 15 Angstrom.");


//Definimos los puntos que enmarcan los tubos
//We define the points that frame the tubes -- bounding boxes, maybe?
			pto2D vlong1 = new pto2D (pB1.y, -pB1.x);
			pto2D vlong2 = new pto2D (-pB2.y, pB2.x);

			pto2D pA1b = vlong1.aversor ().escala (lent1).a2D ();
			pto2D pB1b = pB1.mas (pA1b);
			pto2D pA2b = vlong2.aversor ().escala (lent2).a2D ();
			pto2D pB2b = pB2.mas (pA2b);
			pto2D pC1p = pC1.proyeccplano (pA1b).a2D ();
			pto2D pC2p = pC2.proyeccplano (pA2b).a2D ();
			pto2D pC1b = pC1p.mas (pA1b);
			pto2D pC2b = pC2p.mas (pA2b);


			double maxcx1 = Math.max (Math.max (pA1.x, pB1.x),
						  Math.max (pA1b.x, pB1b.x));
			double maxcy1 = Math.max (Math.max (pA1.y, pB1.y),
						  Math.max (pA1b.y, pB1b.y));
			double mincx1 = Math.min (Math.min (pA1.x, pB1.x),
						  Math.min (pA1b.x, pB1b.x));
			double mincy1 = Math.min (Math.min (pA1.y, pB1.y),
						  Math.min (pA1b.y, pB1b.y));

			double maxcx2 = Math.max (Math.max (pA2.x, pB2.x),
						  Math.max (pA2b.x, pB2b.x));
			double maxcy2 = Math.max (Math.max (pA2.y, pB2.y),
						  Math.max (pA2b.y, pB2b.y));
			double mincx2 = Math.min (Math.min (pA2.x, pB2.x),
						  Math.min (pA2b.x, pB2b.x));
			double mincy2 = Math.min (Math.min (pA2.y, pB2.y),
						  Math.min (pA2b.y, pB2b.y));

			txlog.setText (txlog.getText () + "\n\nStarting 1st tube construction. ");
			txlog.setText (txlog.getText () + "Filling 1st tube planar projection ");

			MoleculaB cachot1 = new MoleculaB ();


			int sty1 = (int) Math.round ((mincy1 - 3.0) / A / r3);
			int stx1 = (int) Math.round ((mincx1 - 3.0) / A);
			int enx1 = (int) Math.round ((maxcx1 + 3.0) / A);
			int eny1 = (int) Math.round ((maxcy1 + 3.0) / A / r3);

			int nummax1 = (int) (4 * (maxcx1 - mincx1) * (maxcy1 - mincy1) / A / A / r3);
			int[][] posis1 = new int[3][nummax1];
			int indi1 = 0;
			for (int cely = sty1; cely <= eny1; cely++)	//recorrido vertical
				for (int celx = stx1; celx <= enx1; celx++)	//recorrido horizontal
				{
					pto2D at1 = new pto2D (celx * A,
							       cely * A * r3 + A / r3);
					pto2D at2 = new pto2D (celx * A,
							       cely * A * r3 - A / r3);
					pto2D at3 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 + A / 2.0 / r3);
					pto2D at4 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 - A / 2.0 / r3);
					if (at1.dentro4l (pA1, pC1, pC1b, pA1b)
					    || at1.dentro4l (pC1, pB1, pB1b, pC1b)) {
						cachot1.addVert (at1, 6);
						posis1[0][indi1] = 1;
						posis1[1][indi1] = celx - cely;
						posis1[2][indi1] = cely * 2;
						indi1++;
					}	//HJ.addVert(at1,7);
					if (at2.dentro4l (pA1, pC1, pC1b, pA1b)
					    || at2.dentro4l (pC1, pB1, pB1b, pC1b)) {
						cachot1.addVert (at2, 6);
						posis1[0][indi1] = 2;
						posis1[1][indi1] = celx - cely + 1;
						posis1[2][indi1] = cely * 2 - 2;
						indi1++;
					}	//HJ.addVert(at2,7);
					if (at3.dentro4l (pA1, pC1, pC1b, pA1b)
					    || at3.dentro4l (pC1, pB1, pB1b, pC1b)) {
						cachot1.addVert (at3, 6);
						posis1[0][indi1] = 2;
						posis1[1][indi1] = celx - cely + 1;
						posis1[2][indi1] = cely * 2 - 1;
						indi1++;
					}	//HJ.addVert(at3,7);
					if (at4.dentro4l (pA1, pC1, pC1b, pA1b)
					    || at4.dentro4l (pC1, pB1, pB1b, pC1b)) {
						cachot1.addVert (at4, 6);
						posis1[0][indi1] = 1;
						posis1[1][indi1] = celx - cely + 1;
						posis1[2][indi1] = cely * 2 - 1;
						indi1++;
					}	//HJ.addVert(at4,7);
				}
			txlog.setText (txlog.getText () + "with " + cachot1.nvert () + " atoms.");

			MoleculaB cachot2 = new MoleculaB ();

			txlog.setText (txlog.getText () + "\nStarting 2nd tube construction. ");
			txlog.setText (txlog.getText () + "Filling 2nd tube planar projection ");

//hay que calcular la macrocelda del comienzo, que debe ser el centro de un hexagono
//it is necessary to calculate the macrocell of the beginning, that must be the center of a hexagon
			int sty2 = (int) Math.round ((mincy2 - 3.0) / A / r3);
			int stx2 = (int) Math.round ((mincx2 - 3.0) / A);
			int enx2 = (int) Math.round ((maxcx2 + 3.0) / A);
			int eny2 = (int) Math.round ((maxcy2 + 3.0) / A / r3);

			int nummax2 = (int) (4 * (maxcx2 - mincx2) * (maxcy2 - mincy2) / A / A / r3);
			int[][] posis2 = new int[3][nummax2];
			int indi2 = 0;
			for (int cely = sty2; cely <= eny2; cely++)	//recorrido vertical,
				for (int celx = stx2; celx <= enx2; celx++)	//recorrido horizontal
				{
					pto2D at1 = new pto2D (celx * A,
							       cely * A * r3 + A / r3);
					pto2D at2 = new pto2D (celx * A,
							       cely * A * r3 - A / r3);
					pto2D at3 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 + A / 2.0 / r3);
					pto2D at4 = new pto2D (celx * A + A / 2.0,
							       cely * A * r3 - A / 2.0 / r3);
					if (at1.dentro4l (pA2, pA2b, pC2b, pC2)
					    || at1.dentro4l (pC2, pC2b, pB2b, pB2)) {
						cachot2.addVert (at1, 6);
						posis2[0][indi2] = 1;
						posis2[1][indi2] = celx - cely;
						posis2[2][indi2] = cely * 2;
						indi2++;
					}	//HJ.addVert(at1,1);
					if (at2.dentro4l (pA2, pA2b, pC2b, pC2)
					    || at2.dentro4l (pC2, pC2b, pB2b, pB2)) {
						cachot2.addVert (at2, 6);
						posis2[0][indi2] = 2;
						posis2[1][indi2] = celx - cely + 1;
						posis2[2][indi2] = cely * 2 - 2;
						indi2++;
					}	//HJ.addVert(at2,1);
					if (at3.dentro4l (pA2, pA2b, pC2b, pC2)
					    || at3.dentro4l (pC2, pC2b, pB2b, pB2)) {
						cachot2.addVert (at3, 6);
						posis2[0][indi2] = 2;
						posis2[1][indi2] = celx - cely + 1;
						posis2[2][indi2] = cely * 2 - 1;
						indi2++;
					}	//HJ.addVert(at3,1);
					if (at4.dentro4l (pA2, pA2b, pC2b, pC2)
					    || at4.dentro4l (pC2, pC2b, pB2b, pB2)) {
						cachot2.addVert (at4, 6);
						posis2[0][indi2] = 1;
						posis2[1][indi2] = celx - cely + 1;
						posis2[2][indi2] = cely * 2 - 1;
						indi2++;
					}	//HJ.addVert(at4,1);
				}
			txlog.setText (txlog.getText () + "with " + cachot2.nvert () + " atoms.");


			double C1 = pC1.proyeccplano (pA1b).modulo ();
			double C2 = pC2.proyeccplano (pA2b).modulo ();
			double C = (C1 + C2) * 0.5;
			double B1 = pB1.modulo () - C1;
			double B2 = pB2.modulo () - C2;
			double B = (B1 + B2) * 0.5;


			double AL1 = pB1.angulocwhasta (pC1);
			double AL2 = pB2.angulocwhasta (pC2);	//pueden ser negativos!!
			double Cy1 = C1 * Math.tan (AL1);
			double Cy2 = C2 * Math.tan (AL2);	//y estos tres tambien!!
			double Cy = (Cy1 + Cy2) * 0.5;

			// the sines deformed after the contraction, change!
			double tanAL1d = Cy1 / C;	//los senos deformados /despues de la contraccion, cambian!
			double tanAL2d = Cy2 / C;	//los senos deformados /despues de la contraccion, cambian!
			double tanBE1d = Cy1 / B;	//los senos deformados /despues de la contraccion, cambian!
			double tanBE2d = Cy2 / B;	//los senos deformados /despues de la contraccion, cambian!
			double tanALM = Cy / C;	//los senos deformados /despues de la contraccion, cambian!
			double tanBEM = Cy / B;	//los senos deformados /despues de la contraccion, cambian!


			double ratio11 = tanAL1d - tanALM;
			double ratio12 = tanBE1d - tanBEM;

			double ratio21 = tanAL2d - tanALM;
			double ratio22 = tanBE2d - tanBEM;
			double R = (B + C) / 2.0 / Math.PI;

			// this is an angle in radians
			double curvaP = 0.4;	//esto es un angulillo en radianes
			double curvaH = 0.4;

			pto3D lead11 = new pto3D ();
			pto3D lead12 = new pto3D ();
			pto3D lead21 = new pto3D ();
			pto3D lead22 = new pto3D ();


			double a2to1 = (C / R);	//en radianes
			double sa = Math.sin (a2to1);
			double ca = Math.cos (a2to1);
			double sp = Math.sin (curvaP);
			double cp = Math.cos (curvaP);
			double sh = Math.sin (curvaH);
			double ch = Math.cos (curvaH);

			if (directo) {
				lead11 = new pto3D (sp, 0.0, cp);	//De primer tubo     P
				lead12 = new pto3D (-ca * sh, -sa * sh, ch);	//2ndo def      H
				lead21 = new pto3D (-sp, 0.0, cp);	//De segundo tubo    P
				lead22 = new pto3D (ca * sh, sa * sh, ch);	//                   H
			} else {
				lead11 = new pto3D (-sh, 0.0, ch);	//De primer tubo     H
				lead12 = new pto3D (ca * sp, sa * sp, cp);	//2ndo def      P  
				lead21 = new pto3D (sh, 0.0, ch);	//De segundo tubo    H
				lead22 = new pto3D (-ca * sp, -sa * sp, cp);	//                   P
			}
			pto3D lt1 = lead11.mas (lead12).aversor ();
			pto3D lt2 = lead21.mas (lead22).aversor ();
			double ratio1x = lt1.x / lt1.z;
			double ratio1y = lt1.y / lt1.z;
			double ratio2x = lt2.x / lt2.z;
			double ratio2y = lt2.y / lt2.z;


			for (int i = 0; i < cachot1.nvert (); i++) {
				pto3D p = ((Atomo) cachot1.susatomos.get (i)).vert;
				double xg = p.proyeccplano (pA1b).modulo ();
				double yg = p.proyeccplano (pB1).modulo ();
				if (p.angulocong (pA1b) <= 90.0)
					yg *= -1;

				double xm = xg;
				double ym = yg;
				double htolq = 0;	//Height to linea queb
				if (xg < C1) {
					xm = xg / C1 * C;
					ym = yg + ratio11 * xm;
					htolq = yg + tanAL1d * xm;
				} else {
					xm = C + B - (B1 + C1 - xg) / B1 * B;
					ym = yg + ratio12 * (B + C - xm);
					htolq = yg + tanBE1d * (B + C - xm);
				}
				double xf = R * Math.cos (Math.PI * 2 * xm / (B + C)) + (ratio1x * htolq);	//*ft1+ratio2x*ft2)
				double yf = R * Math.sin (Math.PI * 2 * xm / (B + C)) + (ratio1y * htolq);	//ft1+ratio2y*ft2)*
				double zf = ym;

				pto3D ptemp = new pto3D (xf, yf, zf);
				int tipoat = posis1[0][i];
				int celdai = posis1[1][i];
				int celdaj = posis1[2][i];

				double co = 0.0;
				if (htolq > -GMI)
					co = 0.0;	//ES el Gap MInimo
				else if (htolq < -GAP)
					co = 1.0;	//ES el Gap de AProximacion;
				else
					co = (zf + GMI) / (-GAP + GMI);	//num siempre creciente, den negativo
					// always increasing number, gives negative

				pto3D pperf = aproxNTchap (ptemp,	//Punto a aproximar
							   tipoat,
							   celdai, celdaj,
							   new pto3D (0, 0,
								      0),
							   i1, j1,
							   lt1,
							   new pto3D (R, 0,
								      0),
							   co);
				if (!HJ.ocupa1 (pperf))
					HJ.addVert (pperf, 6);
			}


			for (int i = 0; i < cachot2.nvert (); i++) {
				pto3D p = ((Atomo) cachot2.susatomos.get (i)).vert;
				double xg = p.proyeccplano (pA2b).modulo ();	//OJO a la costura
				double yg = p.proyeccplano (pB2).modulo ();
				if (p.angulocong (pA2b) >= 90.0)
					yg *= -1;	//los que son negativos

				double xm = xg;
				double ym = yg;
				double htolq = 0;
				if (xg < C2) {
					xm = xg / C2 * C;
					ym = yg + ratio21 * xm;
					htolq = yg + tanAL2d * xm;
				} else {
					xm = C + B - (B2 + C2 - xg) / B2 * B;
					ym = yg + ratio22 * (B + C - xm);
					htolq = yg + tanBE2d * (B + C - xm);
				}


				double xf = R * Math.cos (Math.PI * 2 * xm / (B + C)) + (ratio2x * htolq);	//ft1+ratio2x*ft2)*ym
				double yf = R * Math.sin (Math.PI * 2 * xm / (B + C)) + (ratio2y * htolq);	//ft1+ratio2y*ft2)*ym
				double zf = ym;

				pto3D ptemp = new pto3D (xf, yf, zf);

				int tipoat = posis2[0][i];
				int celdai = posis2[1][i];
				int celdaj = posis2[2][i];


				double co = 0.0;
				if (htolq < GMI)
					co = 0.0;	//ES el Gap MInimo
				else if (htolq > GAP)
					co = 1.0;	//ES el Gap de AProximacion;
				else
					co = (zf - GMI) / (GAP - GMI);

				pto3D pperf = aproxNTchap (ptemp,	//Punto a aproximar
							   tipoat,
							   celdai, celdaj,
							   new pto3D (0, 0,
								      0),
							   i2, j2,
							   lt2,
							   new pto3D (R, 0,
								      0),
							   co);


				if (!HJ.ocupa1 (pperf))
					HJ.addVert (pperf, 6, Color.gray);
			}

			txlog.setText (txlog.getText () + "\n\nStructure (" +
				       i1 + "," + j1 + ")-(" + i2 + "," + j2 + ") completed with " + HJ.nvert () + " atoms");

		}


//final y visualizacin
//end and visualization
		HJ.reconec (1.35);
		txlog.setText (txlog.getText () + "\n\nRefining process: ");

		int eliminados = HJ.depuraconec ();
		txlog.setText (txlog.getText () + eliminados + " bonds removed and ");
		int creados = HJ.remataconec ();
		txlog.setText (txlog.getText () + creados + " bonds added.");



		if (combo.getSelectedItem () == "Hydrogen") {
			HJ.cierraH ();
			HJ.ponconec (1.35);
		} else if (combo.getSelectedItem () == "Nitrogen") {
			HJ.cierraN ();
			HJ.ponconec (1.35);
		}
		HJ.centrar ();
		pan3D.cargarMol (HJ);
		pan3D.repaint ();


	}

	public MoleculaT molT ()
	{
		return HJ;
	}

	private int detectanconc (pto2D pA, pto2D pB, pto2D pC, pto2D pD)
	{
		int nconcav = 0;
//el interior esta por debajo del primer trayecto. si tiene4 concavidades, es el complementario del cuadrado.
		pto2D v1 = pC.menos (pB);
		pto2D v2 = pD.menos (pC);
		pto2D v3 = pA.menos (pD);
		pto2D v4 = pB.menos (pA);

		double a1 = v1.angulocwhasta (v2);
		if (a1 < 0 || a1 > Math.PI)
			nconcav++;
		double a2 = v2.angulocwhasta (v3);
		if (a2 < 0 || a2 > Math.PI)
			nconcav++;
		double a3 = v3.angulocwhasta (v4);
		if (a3 < 0 || a3 > Math.PI)
			nconcav++;
		double a4 = v4.angulocwhasta (v1);
		if (a4 < 0 || a4 > Math.PI)
			nconcav++;


		return nconcav;
	}

	private pto3D aproxNT (pto3D pto,	//pto es el punto a aproximar
			       int atotip, int posi, int posj, pto3D origen, int i, int j, pto3D orient, pto3D inicio, double coef)
	{



		pto3D inicioperp = inicio.proyeccplano (orient);
		pto3D vprolonori = inicio.menos (inicioperp);	//a la fuerza es la proy de inidio sobre la orientacion

		pto3D vx = inicioperp.aversor ();
		pto3D vz = orient.aversor ();
		pto3D vy = vz.prodvect (vx);

		Nanotubo NT = new Nanotubo (i, j);

//contamos los pasos tanto radiales como longitudinales correspondientes a posi, posj

		double deltazet = -posi * NT.deltaz2 + posj * NT.deltaz1;	//revisar si el orden esta bien los deltaz son todos positivos, ojo a los signos
		double deltaphi = posi * NT.deltaphi2 + posj * NT.deltaphi1;	//OJO por motivos historicos, 1 se refiere alvect de la red que esta
// por encima de Quiral,


		double deltazetat = atotip * NT.deltazc ();
		double deltaphiat = atotip * NT.deltaphic ();

		double R = NT.radio ();
		double cdp = Math.cos (deltaphi + deltaphiat);
		double sdp = Math.sin (deltaphi + deltaphiat);

//los tres pasitos
		pto3D vv1 = vz.escala (deltazet + deltazetat);
		pto3D vv2 = vx.escala (R * cdp);
		pto3D vv3 = vy.escala (R * sdp);


		return pto.ptopondcon (origen.mas (vv1).mas (vv2).mas (vv3), coef);

	}



	private pto3D aproxNTchap (pto3D pto,	//pto es el punto a aproximar
				   int atotip, int posi, int posj, pto3D origen, int i, int j, pto3D orient, pto3D inicio, double coef)
	{
		pto3D inicioperp = inicio.proyeccplano (orient);
		pto3D vprolonori = inicio.menos (inicioperp);

		origen = origen.mas (vprolonori);

		pto3D vx = inicioperp.aversor ();
		pto3D vz = orient.aversor ();
		pto3D vy = vz.prodvect (vx);

		Nanotubo NT = new Nanotubo (i, j);


		double deltazet = -posi * NT.deltaz2 + posj * NT.deltaz1;
		double deltaphi = posi * NT.deltaphi2 + posj * NT.deltaphi1;



		double deltazetat = atotip * NT.deltazc ();
		double deltaphiat = atotip * NT.deltaphic ();

		double R = NT.radio ();
		double cdp = Math.cos (deltaphi + deltaphiat);	//
		double sdp = Math.sin (deltaphi + deltaphiat);	//

//los tres pasitos
		pto3D vv1 = vz.escala (deltazet + deltazetat);	//
		pto3D vv2 = vx.escala (R * cdp);
		pto3D vv3 = vy.escala (R * sdp);

		return pto.ptopondcon (origen.mas (vv1).mas (vv2).mas (vv3), coef);
	}


	int naproxatomos (double r1, double l1, double r2, double l2)
	{
		double dens = 0.34;	//atomos opr A cuadrado
		double d1 = Math.PI * 2 * r1;
		double d2 = Math.PI * 2 * r2;

		double rr1 = 6.0 * r1;	//Radios del cono proyectado en el plano
		double rr2 = 6.0 * r2;
		double acono = 0.;
		if (r1 > r2)
			acono = Math.PI * (rr1 * rr1 - rr2 * rr2) / 6.0;
		else
			acono = Math.PI * (rr2 * rr2 - rr1 * rr1) / 6.0;

		return (int) (dens * (d1 * l1 + d2 * l2 + acono));

	}


}