/** * Parity computation for one side of a double bond in a geometric center. * The method needs the 3D coordinates of the double bond atoms (first 2 * arguments) and the coordinates of two substituents (one at each end). * * @param u an atom double bonded to v * @param v an atom double bonded to u * @param x an atom sigma bonded to u * @param w an atom sigma bonded to v * @return the parity of the atoms */ private int parity(Point3d u, Point3d v, Point3d x, Point3d w) { // create three vectors, v->u, v->w and u->x double[] vu = toVector(v, u); double[] vw = toVector(v, w); double[] ux = toVector(u, x); // normal vector (to compare against), the normal vector (n) looks like: // x n w // \ |/ // u = v double[] normal = crossProduct(vu, crossProduct(vu, vw)); // compare the dot products of v->w and u->x, if the signs are the same // they are both pointing the same direction. if a value is close to 0 // then it is at pi/2 radians (i.e. unspecified) however 3D coordinates // are generally discrete and do not normally represent on unspecified // stereo configurations so we don't check this int parity = (int) Math.signum(dot(normal, vw)) * (int) Math.signum(dot(normal, ux)); // invert sign, this then matches with Sp2 double bond parity return parity * -1; }
/** * Parity computation for 3D tetrahedral stereocenters. * * @param atoms the atoms surrounding the central focus atom * @return the parity (winding) */ private int parity(IAtom[] atoms) { if (atoms.length != 4) throw new IllegalArgumentException("incorrect number of atoms"); Point3d[] coordinates = new Point3d[atoms.length]; for (int i = 0; i < atoms.length; i++) { coordinates[i] = atoms[i].getPoint3d(); if (coordinates[i] == null) return 0; } double x1 = coordinates[0].x; double x2 = coordinates[1].x; double x3 = coordinates[2].x; double x4 = coordinates[3].x; double y1 = coordinates[0].y; double y2 = coordinates[1].y; double y3 = coordinates[2].y; double y4 = coordinates[3].y; double z1 = coordinates[0].z; double z2 = coordinates[1].z; double z3 = coordinates[2].z; double z4 = coordinates[3].z; double det = (z1 * det(x2, y2, x3, y3, x4, y4)) - (z2 * det(x1, y1, x3, y3, x4, y4)) + (z3 * det(x1, y1, x2, y2, x4, y4)) - (z4 * det(x1, y1, x2, y2, x3, y3)); return (int) Math.signum(det); }
return null; int parity = parity(carriers); int cfg = parity > 0 ? IStereoElement.LEFT : IStereoElement.RIGHT;
if (!isColinear(focus, terminals)) return null; return null; int parity = parity(neighbors);
/**{@inheritDoc} */ @Override ITetrahedralChirality createTetrahedral(int v, Stereocenters stereocenters) { if (!stereocenters.isStereocenter(v)) return null; IAtom focus = container.getAtom(v); if (hasUnspecifiedParity(focus)) return null; IAtom[] neighbors = new IAtom[4]; neighbors[3] = focus; int n = 0; for (int w : graph[v]) neighbors[n++] = container.getAtom(w); // too few/many neighbors if (n < 3 || n > 4) return null; // TODO: verify valid wedge/hatch configurations using similar procedure // to NonPlanarBonds in the cdk-sdg package int parity = parity(neighbors); Stereo winding = parity > 0 ? Stereo.ANTI_CLOCKWISE : Stereo.CLOCKWISE; return new TetrahedralChirality(focus, neighbors, winding); }
/**{@inheritDoc} */ @Override IDoubleBondStereochemistry createGeometric(int u, int v, Stereocenters stereocenters) { if (hasUnspecifiedParity(container.getAtom(u)) || hasUnspecifiedParity(container.getAtom(v))) return null; int[] us = graph[u]; int[] vs = graph[v]; int x = us[0] == v ? us[1] : us[0]; int w = vs[0] == u ? vs[1] : vs[0]; IAtom uAtom = container.getAtom(u); IAtom vAtom = container.getAtom(v); IAtom uSubstituentAtom = container.getAtom(x); IAtom vSubstituentAtom = container.getAtom(w); if (uAtom.getPoint3d() == null || vAtom.getPoint3d() == null || uSubstituentAtom.getPoint3d() == null || vSubstituentAtom.getPoint3d() == null) return null; int parity = parity(uAtom.getPoint3d(), vAtom.getPoint3d(), uSubstituentAtom.getPoint3d(), vSubstituentAtom.getPoint3d()); Conformation conformation = parity > 0 ? Conformation.OPPOSITE : Conformation.TOGETHER; IBond bond = bondMap.get(u, v); bond.setAtoms(new IAtom[]{uAtom, vAtom}); return new DoubleBondStereochemistry(bond, new IBond[]{bondMap.get(u, x), bondMap.get(v, w),}, conformation); }
/**{@inheritDoc} */ @Override ITetrahedralChirality createTetrahedral(IAtom atom, Stereocenters stereocenters) { return createTetrahedral(container.indexOf(atom), stereocenters); }
/** * Create a stereo element factory for creating stereo elements using 3D * coordinates and depiction labels (up/down, wedge/hatch). * * @param container the structure to create the factory for * @return the factory instance */ public static StereoElementFactory using3DCoordinates(IAtomContainer container) { EdgeToBondMap bondMap = EdgeToBondMap.withSpaceFor(container); int[][] graph = GraphUtil.toAdjList(container, bondMap); return new StereoElementFactory3D(container, graph, bondMap).checkSymmetry(true); }
/**{@inheritDoc} */ @Override IDoubleBondStereochemistry createGeometric(IBond bond, Stereocenters stereocenters) { return createGeometric(container.indexOf(bond.getBegin()), container.indexOf(bond.getEnd()), stereocenters); }