Example #1
0
        /// <summary>
        /// Place ring with user provided angles.
        /// </summary>
        /// <param name="ring">the ring to place.</param>
        /// <param name="ringCenter">center coordinates of the ring.</param>
        /// <param name="bondLength">given bond length.</param>
        /// <param name="startAngles">a map with start angles when drawing the ring.</param>
        public virtual void PlaceRing(IRing ring, Vector2 ringCenter, double bondLength, IReadOnlyDictionary <int, double> startAngles)
        {
            var radius   = GetNativeRingRadius(ring, bondLength);
            var addAngle = 2 * Math.PI / ring.RingSize;

            var     startAtom = ring.Atoms[0];
            Vector2 p         = new Vector2((ringCenter.X + radius), ringCenter.Y);

            startAtom.Point2D = p;
            var startAngle = Math.PI * 0.5;

            // Different ring sizes get different start angles to have visually
            // correct placement
            int ringSize = ring.RingSize;

            startAngle = startAngles[ringSize];

            var bonds = ring.GetConnectedBonds(startAtom);
            // Store all atoms to draw in consecutive order relative to the chosen bond.
            var   atomsToDraw = new List <IAtom>();
            IAtom currentAtom = startAtom;
            IBond currentBond = (IBond)bonds.First();

            for (int i = 0; i < ring.Bonds.Count; i++)
            {
                currentBond = ring.GetNextBond(currentBond, currentAtom);
                currentAtom = currentBond.GetOther(currentAtom);
                atomsToDraw.Add(currentAtom);
            }
            AtomPlacer.PopulatePolygonCorners(atomsToDraw, ringCenter, startAngle, addAngle, radius);
        }
Example #2
0
        /// <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);
                    }
                }
            }
        }
Example #3
0
        /// <summary>
        /// Generated coordinates for a given ring, which is connected to a spiro ring.
        /// The rings share exactly one atom.
        /// </summary>
        /// <param name="ring">The ring to be placed</param>
        /// <param name="sharedAtoms">The atoms of this ring, also members of another ring, which are already placed</param>
        /// <param name="sharedAtomsCenter">The geometric center of these atoms</param>
        /// <param name="ringCenterVector">A vector pointing the center of the new ring</param>
        /// <param name="bondLength">The standard bond length</param>
        public virtual void PlaceSpiroRing(IRing ring, IAtomContainer sharedAtoms, Vector2 sharedAtomsCenter, Vector2 ringCenterVector, double bondLength)
        {
            var startAtom = sharedAtoms.Atoms[0];
            var mBonds    = Molecule.GetConnectedBonds(sharedAtoms.Atoms[0]).ToReadOnlyList();
            var degree    = mBonds.Count;

            Debug.WriteLine($"placeSpiroRing: D={degree}");
            // recalculate the ringCentreVector
            if (degree != 4)
            {
                int numPlaced = 0;
                foreach (var bond in mBonds)
                {
                    var nbr = bond.GetOther(sharedAtoms.Atoms[0]);
                    if (!nbr.IsPlaced)
                    {
                        continue;
                    }
                    numPlaced++;
                }
                if (numPlaced == 2)
                {
                    // nudge the shared atom such that bond lengths will be
                    // equal
                    startAtom.Point2D += ringCenterVector;
                    sharedAtomsCenter += ringCenterVector;
                }
                var theta = Math.PI - (2 * Math.PI / (degree / 2));
                Rotate(ringCenterVector, theta);
            }

            var radius     = GetNativeRingRadius(ring, bondLength);
            var ringCenter = sharedAtomsCenter;

            if (degree == 4)
            {
                ringCenterVector  = Vector2.Normalize(ringCenterVector);
                ringCenterVector *= radius;
            }
            else
            {
                // spread things out a little for multiple spiro centres
                ringCenterVector  = Vector2.Normalize(ringCenterVector);
                ringCenterVector *= (2 * radius);
            }

            ringCenter += ringCenterVector;
            var addAngle = 2 * Math.PI / ring.RingSize;

            var currentAtom = startAtom;
            var startAngle  = GeometryUtil.GetAngle(
                startAtom.Point2D.Value.X - ringCenter.X,
                startAtom.Point2D.Value.Y - ringCenter.Y);

            // Get one bond connected to the spiro bridge atom. It doesn't matter in which direction we draw.
            var rBonds = ring.GetConnectedBonds(startAtom);

            var currentBond = rBonds.First();

            var atomsToDraw = new List <IAtom>();

            // Store all atoms to draw in consequtive order relative to the chosen bond.
            for (int i = 0; i < ring.Bonds.Count; i++)
            {
                currentBond = ring.GetNextBond(currentBond, currentAtom);
                currentAtom = currentBond.GetOther(currentAtom);
                if (!currentAtom.Equals(startAtom))
                {
                    atomsToDraw.Add(currentAtom);
                }
            }
            Debug.WriteLine($"currentAtom  {currentAtom}");
            Debug.WriteLine($"startAtom  {startAtom}");

            AtomPlacer.PopulatePolygonCorners(atomsToDraw, ringCenter, startAngle, addAngle, radius);
        }