/// <summary> /// Layout all rings in the given RingSet that are connected to a given Ring /// </summary> /// <param name="rs">The RingSet to be searched for rings connected to Ring</param> /// <param name="ring">The Ring for which all connected rings in RingSet are to be layed out.</param> public void PlaceConnectedRings(IRingSet rs, IRing ring, int handleType, double bondLength) { var connectedRings = rs.GetConnectedRings(ring); // Debug.WriteLine(rs.ReportRingList(Molecule)); foreach (var container in connectedRings) { IRing connectedRing = (IRing)container; if (!connectedRing.IsPlaced) { // Debug.WriteLine(ring.ToString(Molecule)); // Debug.WriteLine(connectedRing.ToString(Molecule)); IAtomContainer sharedAtoms = AtomContainerManipulator.GetIntersection(ring, connectedRing); int numSharedAtoms = sharedAtoms.Atoms.Count; Debug.WriteLine("placeConnectedRings-> connectedRing: " + (ring.ToString())); if ((numSharedAtoms == 2 && handleType == Fused) || (numSharedAtoms == 1 && handleType == Spiro) || (numSharedAtoms > 2 && handleType == Bridged)) { Vector2 sharedAtomsCenter = GeometryUtil.Get2DCenter(sharedAtoms); Vector2 oldRingCenter = GeometryUtil.Get2DCenter(ring); Vector2 tempVector = sharedAtomsCenter; Vector2 newRingCenterVector = tempVector; newRingCenterVector -= oldRingCenter; // zero (or v. small ring center) if (Math.Abs(newRingCenterVector.X) < 0.001 && Math.Abs(newRingCenterVector.Y) < 0.001) { // first see if we can use terminal bonds IAtomContainer terminalOnly = Molecule.Builder.NewAtomContainer(); foreach (IAtom atom in ring.Atoms) { if (ring.GetConnectedBonds(atom).Count() == 1) { terminalOnly.Atoms.Add(atom); } } if (terminalOnly.Atoms.Count == 2) { newRingCenterVector = GeometryUtil.Get2DCenter(terminalOnly); newRingCenterVector = oldRingCenter; connectedRing.SetProperty(RingPlacer.SnapHint, true); } else { // project coordinates on 12 axis (30 degree snaps) and choose one with most spread Vector2 vec = new Vector2(0, 1); double bestLen = -double.MaxValue; for (int i = 0; i < 12; i++) { Vector2 orth = new Vector2(-vec.Y, vec.X); orth = Vector2.Normalize(orth); double min = double.MaxValue, max = -double.MaxValue; foreach (IAtom atom in sharedAtoms.Atoms) { // s: scalar projection double s = Vector2.Dot(orth, atom.Point2D.Value); if (s < min) { min = s; } if (s > max) { max = s; } } double len = max - min; if (len > bestLen) { bestLen = len; newRingCenterVector = vec; } Rotate(vec, RAD_30); } } } Vector2 oldRingCenterVector = newRingCenterVector; Debug.WriteLine($"placeConnectedRing -> tempVector: {tempVector}, tempVector.Length: {tempVector.Length()}"); Debug.WriteLine($"placeConnectedRing -> bondCenter: {sharedAtomsCenter}"); Debug.WriteLine($"placeConnectedRing -> oldRingCenterVector.Length: {oldRingCenterVector.Length()}"); Debug.WriteLine($"placeConnectedRing -> newRingCenterVector.Length: {newRingCenterVector.Length()}"); Vector2 tempPoint = sharedAtomsCenter; tempPoint += newRingCenterVector; PlaceRing(connectedRing, sharedAtoms, sharedAtomsCenter, newRingCenterVector, bondLength); connectedRing.IsPlaced = true; PlaceConnectedRings(rs, connectedRing, handleType, bondLength); } } } }
/// <summary> /// Select the best scoring template + offset for the given macrocycle. /// </summary> /// <param name="macrocycle">macrocycle</param> /// <param name="ringset">entire ring system</param> /// <param name="wind">winding of ring CW/CCW</param> /// <param name="winding">winding of each turn in the ring</param> /// <returns>the best scoring configuration</returns> private static MacroScore BestScore(IRing macrocycle, IRingSet ringset, int wind, int[] winding) { var numAtoms = macrocycle.Atoms.Count; var heteroIdxs = new List <int>(); var ringAttachs = new List <IList <int> >(); // hetero atoms for (int i = 0; i < numAtoms; i++) { if (macrocycle.Atoms[i].AtomicNumber != 6) { heteroIdxs.Add(i); } } foreach (var other in ringset) { if (other == macrocycle) { continue; } var shared = AtomContainerManipulator.GetIntersection(macrocycle, other); if (shared.Atoms.Count >= 2 && shared.Atoms.Count <= 4) { ringAttachs.Add(GetAttachedInOrder(macrocycle, shared)); } } // convex and concave are relative var convex = wind; var concave = -wind; MacroScore best = null; for (int i = 0; i < winding.Length; i++) { // score ring attachs int nRingClick = 0; foreach (var ringAttach in ringAttachs) { int r1, r2, r3, r4; switch (ringAttach.Count) { case 2: r1 = (ringAttach[0] + i) % numAtoms; r2 = (ringAttach[1] + i) % numAtoms; if (winding[r1] == winding[r2]) { if (winding[r1] == convex) { nRingClick += 5; } else { nRingClick++; } } break; case 3: r1 = (ringAttach[0] + i) % numAtoms; r2 = (ringAttach[1] + i) % numAtoms; r3 = (ringAttach[2] + i) % numAtoms; if (winding[r1] == convex && winding[r2] == concave && winding[r3] == convex) { nRingClick += 5; } else if (winding[r1] == concave && winding[r2] == convex && winding[r3] == concave) { nRingClick++; } break; case 4: r1 = (ringAttach[0] + i) % numAtoms; r2 = (ringAttach[1] + i) % numAtoms; r3 = (ringAttach[2] + i) % numAtoms; r4 = (ringAttach[3] + i) % numAtoms; if (winding[r1] == convex && winding[r2] == concave && winding[r3] == concave && winding[r4] == convex) { nRingClick++; } else if (winding[r1] == concave && winding[r2] == convex && winding[r3] == convex && winding[r4] == concave) { nRingClick++; } break; } } // score hetero atoms in concave positions int nConcaveHetero = 0; foreach (var heteroIdx in heteroIdxs) { var k = (heteroIdx + i) % numAtoms; if (winding[k] == concave) { nConcaveHetero++; } } int nCorrectStereo = 0; int nIncorrectStereo = 0; foreach (var se in macrocycle.StereoElements) { if (se.Class == StereoClass.CisTrans) { var bond = (IBond)se.Focus; var beg = bond.Begin; var end = bond.End; StereoConfigurations cfg; if (winding[(macrocycle.Atoms.IndexOf(beg) + i) % numAtoms] == winding[(macrocycle.Atoms.IndexOf(end) + i) % numAtoms]) { cfg = StereoConfigurations.Together; } else { cfg = StereoConfigurations.Opposite; } if (cfg == se.Configure) { nCorrectStereo++; } else { nIncorrectStereo++; } } } var score = new MacroScore(i, nConcaveHetero, nCorrectStereo, nRingClick); if (score.CompareTo(best) < 0) { best = score; } } return(best); }