Beispiel #1
0
        /// <summary>
        /// Recognise the cyclic carbohydrate projections.
        /// </summary>
        /// <param name="projections">the types of projections to recognise</param>
        /// <returns>recognised stereocenters</returns>
        public IEnumerable <IStereoElement <IChemObject, IChemObject> > Recognise(ICollection <Projection> projections)
        {
            if (!projections.Contains(Projection.Haworth) && !projections.Contains(Projection.Chair))
            {
                yield break;
            }

            var ringSearch = new RingSearch(container, graph);

            foreach (var isolated in ringSearch.Isolated())
            {
                if (isolated.Length < 5 || isolated.Length > 7)
                {
                    continue;
                }

                var cycle = Arrays.CopyOf(GraphUtil.Cycle(graph, isolated),
                                          isolated.Length);

                var points     = CoordinatesOfCycle(cycle, container);
                var turns      = GetTurns(points);
                var projection = WoundProjection.OfTurns(turns);

                if (!projections.Contains(projection.Projection))
                {
                    continue;
                }

                // ring is not aligned correctly for Haworth
                if (projection.Projection == Projection.Haworth && !CheckHaworthAlignment(points))
                {
                    continue;
                }

                var horizontalXy = HorizontalOffset(points, turns, projection.Projection);

                // near vertical, should also flag as potentially ambiguous
                if (1 - Math.Abs(horizontalXy.Y) < QuartCardinalityThreshold)
                {
                    continue;
                }

                var above = (int[])cycle.Clone();
                var below = (int[])cycle.Clone();

                if (!AssignSubstituents(cycle, above, below, projection, horizontalXy))
                {
                    continue;
                }

                foreach (var center in NewTetrahedralCenters(cycle, above, below, projection))
                {
                    yield return(center);
                }
            }

            yield break;
        }
Beispiel #2
0
        /// <summary>
        /// Create the tetrahedral stereocenters for the provided cycle.
        /// </summary>
        /// <param name="cycle">vertices in projected cycle</param>
        /// <param name="above">vertices above the cycle</param>
        /// <param name="below">vertices below the cycle</param>
        /// <param name="type">type of projection</param>
        /// <returns>zero of more stereocenters</returns>
        private IReadOnlyList <ITetrahedralChirality> NewTetrahedralCenters(int[] cycle, int[] above, int[] below, WoundProjection type)
        {
            var centers = new List <ITetrahedralChirality>(cycle.Length);

            for (int i = 1; i <= cycle.Length; i++)
            {
                int prev = cycle[i - 1];
                int curr = cycle[i % cycle.Length];
                int next = cycle[(i + 1) % cycle.Length];

                int up   = above[i % cycle.Length];
                int down = below[i % cycle.Length];

                if (!stereocenters.IsStereocenter(curr))
                {
                    continue;
                }

                // Any wedge or hatch bond causes us to exit, this may still be
                // a valid projection. Currently it can cause a collision with
                // one atom have two tetrahedral stereo elements.
                if (!IsPlanarSigmaBond(bonds[curr, prev]) ||
                    !IsPlanarSigmaBond(bonds[curr, next]) ||
                    (up != curr && !IsPlanarSigmaBond(bonds[curr, up])) ||
                    (down != curr && !IsPlanarSigmaBond(bonds[curr, down])))
                {
                    return(Array.Empty <ITetrahedralChirality>());
                }

                centers.Add(new TetrahedralChirality(container.Atoms[curr],
                                                     new IAtom[] { container.Atoms[up],
                                                                   container.Atoms[prev],
                                                                   container.Atoms[down],
                                                                   container.Atoms[next] },
                                                     type.Winding
                                                     ));
            }

            return(centers);
        }
Beispiel #3
0
        /// <summary>
        /// Given a projected cycle, assign the exocyclic substituents to being above
        /// of below the projection. For Haworth projections, the substituents must
        /// be directly up or down (within some threshold).
        /// </summary>
        /// <param name="cycle">vertices that form a cycle</param>
        /// <param name="above">vertices that will be above the cycle (filled by method)</param>
        /// <param name="below">vertices that will be below the cycle (filled by method)</param>
        /// <param name="projection">the type of projection</param>
        /// <param name="horizontalXy">offset from the horizontal axis</param>
        /// <returns>assignment okay (true), not okay (false)</returns>
        private bool AssignSubstituents(int[] cycle,
                                        int[] above,
                                        int[] below,
                                        WoundProjection projection,
                                        Vector2 horizontalXy)
        {
            bool haworth = projection.Projection == Projection.Haworth;

            int found = 0;

            for (int i = 1; i <= cycle.Length; i++)
            {
                int j = i % cycle.Length;

                int prev = cycle[i - 1];
                int curr = cycle[j];
                int next = cycle[(i + 1) % cycle.Length];

                // get the substituents not in the ring (i.e. excl. prev and next)
                int[] ws = Filter(graph[curr], prev, next);

                if (ws.Length > 2 || ws.Length < 1)
                {
                    continue;
                }

                var centerXy = container.Atoms[curr].Point2D.Value;

                // determine the direction of each substituent
                foreach (var w in ws)
                {
                    var otherXy   = container.Atoms[w].Point2D.Value;
                    var direction = ObtainDirection(centerXy, otherXy, horizontalXy, haworth);

                    switch (direction)
                    {
                    case Direction.Up:
                        if (above[j] != curr)
                        {
                            return(false);
                        }
                        above[j] = w;
                        break;

                    case Direction.Down:
                        if (below[j] != curr)
                        {
                            return(false);
                        }
                        below[j] = w;
                        break;

                    case Direction.Other:
                        return(false);
                    }
                }

                if (above[j] != curr || below[j] != curr)
                {
                    found++;
                }
            }

            // must have at least 2 that look projected for Haworth
            return(found > 1 || projection.Projection != Projection.Haworth);
        }