/**
  * Adds 3D coordinates for singly-bonded ligands of a reference atom (A). Initially designed for
  * hydrogens. The ligands of refAtom are identified and those with 3D coordinates used to generate
  * the new points. (This allows structures with partially known 3D coordinates to be used, as when
  * groups are added.) "Bent" and "non-planar" groups can be formed by taking a subset of the
  * calculated points. Thus R-NH2 could use 2 of the 3 points calculated from (1,iii) nomenclature:
  * A is point to which new ones are "attached". A may have ligands B, C... B may have ligands J,
  * K.. points X1, X2... are returned The cases (see individual routines, which use idealised
  * geometry by default): (0) zero ligands of refAtom. The resultant points are randomly oriented:
  * (i) 1 points required; +x,0,0 (ii) 2 points: use +x,0,0 and -x,0,0 (iii) 3 points: equilateral
  * triangle in xy plane (iv) 4 points x,x,x, x,-x,-x, -x,x,-x, -x,-x,x (1a) 1 ligand(B) of refAtom
  * which itself has a ligand (J) (i) 1 points required; vector along AB vector (ii) 2 points: 2
  * vectors in ABJ plane, staggered and eclipsed wrt J (iii) 3 points: 1 staggered wrt J, the
  * others +- gauche wrt J (1b) 1 ligand(B) of refAtom which has no other ligands. A random J is
  * generated and (1a) applied (2) 2 ligands(B, C) of refAtom A (i) 1 points required; vector in
  * ABC plane bisecting AB, AC. If ABC is linear, no points (ii) 2 points: 2 vectors at angle ang,
  * whose resultant is 2i (3) 3 ligands(B, C, D) of refAtom A (i) 1 points required; if A, B, C, D
  * coplanar, no points. else vector is resultant of BA, CA, DA
  *
  * <p>fails if atom itself has no coordinates or >4 ligands
  *
  * @param atomContainer describing the ligands of refAtom. It could be the whole molecule, or
  *     could be a selected subset of ligands
  * @param refAtom (A) to which new ligands coordinates could be added
  * @param length A-X length
  * @param angle B-A-X angle (used in certain cases)
  * @return Point3D[] points calculated. If request could not be fulfilled (e.g. too many atoms, or
  *     strange geometry, returns empty array (zero length, not null)
  * @cdk.keyword coordinate generation
  */
 public static Point3d[] calculate3DCoordinatesForLigands(
     AtomContainer atomContainer, IAtom refAtom, int nwanted, double length, double angle) {
   Point3d newPoints[] = new Point3d[0];
   Point3d aPoint = refAtom.getPoint3d();
   // get ligands
   List connectedAtoms = atomContainer.getConnectedAtomsList(refAtom);
   if (connectedAtoms == null) {
     return newPoints;
   }
   int nligands = connectedAtoms.size();
   AtomContainer ligandsWithCoords = new AtomContainer();
   for (int i = 0; i < nligands; i++) {
     Atom ligand = (Atom) connectedAtoms.get(i);
     if (ligand.getPoint3d() != null) {
       ligandsWithCoords.addAtom(ligand);
     }
   }
   int nwithCoords = ligandsWithCoords.getAtomCount();
   // too many ligands at present
   if (nwithCoords > 3) {
     return newPoints;
   }
   if (nwithCoords == 0) {
     newPoints = calculate3DCoordinates0(refAtom.getPoint3d(), nwanted, length);
   } else if (nwithCoords == 1) {
     // ligand on A
     IAtom bAtom = ligandsWithCoords.getAtom(0);
     connectedAtoms = ligandsWithCoords.getConnectedAtomsList(bAtom);
     // does B have a ligand (other than A)
     Atom jAtom = null;
     for (int i = 0; i < connectedAtoms.size(); i++) {
       Atom connectedAtom = (Atom) connectedAtoms.get(i);
       if (!connectedAtom.equals(refAtom)) {
         jAtom = connectedAtom;
         break;
       }
     }
     newPoints =
         calculate3DCoordinates1(
             aPoint,
             bAtom.getPoint3d(),
             (jAtom != null) ? jAtom.getPoint3d() : null,
             nwanted,
             length,
             angle);
   } else if (nwithCoords == 2) {
     Point3d bPoint = ligandsWithCoords.getAtom(0).getPoint3d();
     Point3d cPoint = ligandsWithCoords.getAtom(1).getPoint3d();
     newPoints = calculate3DCoordinates2(aPoint, bPoint, cPoint, nwanted, length, angle);
   } else if (nwithCoords == 3) {
     Point3d bPoint = ligandsWithCoords.getAtom(0).getPoint3d();
     Point3d cPoint = ligandsWithCoords.getAtom(1).getPoint3d();
     Point3d dPoint = ligandsWithCoords.getAtom(2).getPoint3d();
     newPoints = new Point3d[1];
     newPoints[0] = calculate3DCoordinates3(aPoint, bPoint, cPoint, dPoint, length);
   }
   return newPoints;
 }
 /**
  * Generate coordinates for all atoms which are singly bonded and have no coordinates. This is
  * useful when hydrogens are present but have no coords. It knows about C, O, N, S only and will
  * give tetrahedral or trigonal geometry elsewhere. Bond lengths are computed from covalent radii
  * if available. Angles are tetrahedral or trigonal
  *
  * @param atomContainer the set of atoms involved
  * @cdk.keyword coordinate calculation
  * @cdk.keyword 3D model
  */
 public static void add3DCoordinates1(AtomContainer atomContainer) {
   // atoms without coordinates
   AtomContainer noCoords = new AtomContainer();
   // get vector of possible referenceAtoms?
   AtomContainer refAtoms = new AtomContainer();
   for (int i = 0; i < atomContainer.getAtomCount(); i++) {
     IAtom atom = atomContainer.getAtom(i);
     // is this atom without 3D coords, and has only one ligand?
     if (atom.getPoint3d() == null) {
       java.util.List connectedAtoms = atomContainer.getConnectedAtomsList(atom);
       if (connectedAtoms.size() == 1) {
         IAtom refAtom = (IAtom) connectedAtoms.get(0);
         ;
         if (refAtom.getPoint3d() != null) {
           refAtoms.addAtom(refAtom);
           // store atoms with no coords and ref atoms in a
           // single container
           noCoords.addAtom(atom);
           noCoords.addAtom(refAtom);
           // bond is required to extract ligands
           noCoords.addBond(new Bond(atom, refAtom, CDKConstants.BONDORDER_SINGLE));
         }
       }
     }
   }
   // now add coordinates to ligands of reference atoms
   // use default length of 1.0, which can be adjusted later
   double length = 1.0;
   double angle = TETRAHEDRAL_ANGLE;
   for (int i = 0; i < refAtoms.getAtomCount(); i++) {
     IAtom refAtom = refAtoms.getAtom(i);
     java.util.List noCoordLigands = noCoords.getConnectedAtomsList(refAtom);
     int nLigands = noCoordLigands.size();
     int nwanted = nLigands;
     String elementType = refAtom.getSymbol();
     // try to deal with lone pairs on small hetero
     if (elementType.equals("N") || elementType.equals("O") || elementType.equals("S")) {
       nwanted = 3;
     }
     Point3d[] newPoints =
         calculate3DCoordinatesForLigands(atomContainer, refAtom, nwanted, length, angle);
     for (int j = 0; j < nLigands; j++) {
       IAtom ligand = (IAtom) noCoordLigands.get(j);
       Point3d newPoint = rescaleBondLength(refAtom, ligand, newPoints[j]);
       ligand.setPoint3d(newPoint);
     }
   }
 }