示例#1
0
        /// <summary>
        /// Completes the layout of a partially laid out ring.
        /// </summary>
        /// <param name="rset">ring set</param>
        /// <param name="ring">the ring to complete</param>
        /// <param name="bondLength">the bond length</param>
        internal bool CompletePartiallyPlacedRing(IRingSet rset, IRing ring, double bondLength)
        {
            if (ring.IsPlaced)
            {
                return(true);
            }
            IRing partiallyPlacedRing = Molecule.Builder.NewRing();

            foreach (IAtom atom in ring.Atoms)
            {
                if (atom.Point2D != null)
                {
                    atom.IsPlaced = true;
                }
            }
            AtomPlacer.CopyPlaced(partiallyPlacedRing, ring);

            if (partiallyPlacedRing.Atoms.Count > 1 &&
                partiallyPlacedRing.Atoms.Count < ring.Atoms.Count)
            {
                PlaceConnectedRings(rset, partiallyPlacedRing, RingPlacer.Fused, bondLength);
                PlaceConnectedRings(rset, partiallyPlacedRing, RingPlacer.Bridged, bondLength);
                PlaceConnectedRings(rset, partiallyPlacedRing, RingPlacer.Spiro, bondLength);
                ring.IsPlaced = true;
                return(true);
            }
            else
            {
                return(false);
            }
        }
示例#2
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);
        }
示例#3
0
        public void TriangleTest()
        {
            List <IAtom> atoms = new List <IAtom>
            {
                new Atom("C"),
                new Atom("C"),
                new Atom("C")
            };
            AtomPlacer placer = new AtomPlacer();

            AtomPlacer.PopulatePolygonCorners(atoms, Vector2.Zero, 0, 10, 10);
            foreach (var atom in atoms)
            {
                Assert.IsNotNull(atom.Point2D);
            }
        }
示例#4
0
        public void EmptyAtomsListTest()
        {
            List <IAtom> atoms = new List <IAtom>();
            // switch on debugging, to see if NPE is thrown
            AtomPlacer placer    = new AtomPlacer();
            bool       npeThrown = false;

            try
            {
                AtomPlacer.PopulatePolygonCorners(atoms, Vector2.Zero, 0, 10, 10);
            }
            catch (NullReferenceException)
            {
                npeThrown = true;
            }
            Assert.IsFalse(npeThrown, "Null pointer for emp.Y atoms list");
        }
示例#5
0
        /// <summary>
        /// Positions the aliphatic substituents of a ring system
        /// </summary>
        /// <param name="rs">The RingSystem for which the substituents are to be laid out</param>
        /// <returns>A list of atoms that where laid out</returns>
        public IAtomContainer PlaceRingSubstituents(IRingSet rs, double bondLength)
        {
            Debug.WriteLine("RingPlacer.PlaceRingSubstituents() start");
            IRing          ring             = null;
            IAtom          atom             = null;
            IAtomContainer unplacedPartners = rs.Builder.NewAtomContainer();
            IAtomContainer sharedAtoms      = rs.Builder.NewAtomContainer();
            IAtomContainer primaryAtoms     = rs.Builder.NewAtomContainer();
            IAtomContainer treatedAtoms     = rs.Builder.NewAtomContainer();

            for (int j = 0; j < rs.Count; j++)
            {
                ring = (IRing)rs[j]; // Get the j-th Ring in RingSet rs
                for (int k = 0; k < ring.Atoms.Count; k++)
                {
                    unplacedPartners.RemoveAllElements();
                    sharedAtoms.RemoveAllElements();
                    primaryAtoms.RemoveAllElements();
                    atom = ring.Atoms[k];
                    var rings = rs.GetRings(atom);
                    var centerOfRingGravity = GeometryUtil.Get2DCenter(rings);
                    AtomPlacer.PartitionPartners(atom, unplacedPartners, sharedAtoms);
                    AtomPlacer.MarkNotPlaced(unplacedPartners);
                    try
                    {
                        for (int f = 0; f < unplacedPartners.Atoms.Count; f++)
                        {
                            Debug.WriteLine("placeRingSubstituents->unplacedPartners: "
                                            + (Molecule.Atoms.IndexOf(unplacedPartners.Atoms[f]) + 1));
                        }
                    }
                    catch (Exception)
                    {
                    }

                    treatedAtoms.Add(unplacedPartners);
                    if (unplacedPartners.Atoms.Count > 0)
                    {
                        AtomPlacer.DistributePartners(atom, sharedAtoms, centerOfRingGravity, unplacedPartners, bondLength);
                    }
                }
            }
            Debug.WriteLine("RingPlacer.PlaceRingSubstituents() end");
            return(treatedAtoms);
        }
示例#6
0
        public void Cumulated_x3()
        {
            var m = new AtomContainer();

            m.Atoms.Add(Atom("C", 3));
            m.Atoms.Add(Atom("C", 1));
            m.Atoms.Add(Atom("C", 0));
            m.Atoms.Add(Atom("C", 0));
            m.Atoms.Add(Atom("C", 1));
            m.Atoms.Add(Atom("C", 3));
            m.AddBond(m.Atoms[0], m.Atoms[1], BondOrder.Single);
            m.AddBond(m.Atoms[1], m.Atoms[2], BondOrder.Double);
            m.AddBond(m.Atoms[2], m.Atoms[3], BondOrder.Double);
            m.AddBond(m.Atoms[3], m.Atoms[4], BondOrder.Double);
            m.AddBond(m.Atoms[4], m.Atoms[5], BondOrder.Single);
            m.Atoms[0].Point2D  = Vector2.Zero;
            m.Atoms[0].IsPlaced = true;

            AtomPlacer atomPlacer = new AtomPlacer {
                Molecule = m
            };
            var v = new Vector2(0, 1.5);

            atomPlacer.PlaceLinearChain(m, ref v, 1.5);

            Vector2 p1 = m.Atoms[1].Point2D.Value;
            Vector2 p2 = m.Atoms[2].Point2D.Value;
            Vector2 p3 = m.Atoms[3].Point2D.Value;
            Vector2 p4 = m.Atoms[4].Point2D.Value;

            Vector2 p2p1 = new Vector2(p1.X - p2.X, p1.Y - p2.Y);
            Vector2 p2p3 = new Vector2(p3.X - p2.X, p3.Y - p2.Y);
            Vector2 p3p2 = new Vector2(p2.X - p3.X, p2.Y - p3.Y);
            Vector2 p3p4 = new Vector2(p4.X - p3.X, p4.Y - p3.Y);

            p2p1 = Vector2.Normalize(p2p1);
            p2p3 = Vector2.Normalize(p2p3);
            p3p2 = Vector2.Normalize(p3p2);
            p3p4 = Vector2.Normalize(p3p4);

            //Assert.AreEqual(Math.PI, Math.Acos(p2p1.X * p2p3.X + p2p1.Y * p2p3.Y), 0.05);
            //Assert.AreEqual(Math.PI, Math.Acos(p3p2.X * p3p4.X + p3p2.Y * p3p4.Y), 0.05);
            Assert.AreEqual(-1, p2p1.X * p2p3.X + p2p1.Y * p2p3.Y, 0.001);
            Assert.AreEqual(-1, p3p2.X * p3p4.X + p3p2.Y * p3p4.Y, 0.001);
        }
示例#7
0
        public void Cumulated_x2()
        {
            var m = new AtomContainer();

            m.Atoms.Add(Atom("C", 3));
            m.Atoms.Add(Atom("C", 1));
            m.Atoms.Add(Atom("C", 0));
            m.Atoms.Add(Atom("C", 1));
            m.Atoms.Add(Atom("C", 3));
            m.AddBond(m.Atoms[0], m.Atoms[1], BondOrder.Single);
            m.AddBond(m.Atoms[1], m.Atoms[2], BondOrder.Double);
            m.AddBond(m.Atoms[2], m.Atoms[3], BondOrder.Double);
            m.AddBond(m.Atoms[3], m.Atoms[4], BondOrder.Single);
            m.Atoms[0].Point2D  = Vector2.Zero;
            m.Atoms[0].IsPlaced = true;

            AtomPlacer atomPlacer = new AtomPlacer {
                Molecule = m
            };
            var v = new Vector2(0, 1.5);

            atomPlacer.PlaceLinearChain(m, ref v, 1.5);

            Vector2 p1 = m.Atoms[1].Point2D.Value;
            Vector2 p2 = m.Atoms[2].Point2D.Value;
            Vector2 p3 = m.Atoms[3].Point2D.Value;

            Vector2 p2p1 = new Vector2(p1.X - p2.X, p1.Y - p2.Y);
            Vector2 p2p3 = new Vector2(p3.X - p2.X, p3.Y - p2.Y);

            p2p1 = Vector2.Normalize(p2p1);
            p2p3 = Vector2.Normalize(p2p3);

            double round = (float)(p2p1.X * p2p3.X + p2p1.Y * p2p3.Y); // rounding the value to avoid over 1 value.
            double theta = Math.Acos(round);

            Assert.AreEqual(Math.PI, theta, 0.05);
        }
示例#8
0
        /// <summary>
        /// Generated coordinates for a given ring, which is fused to another ring.
        /// The rings share exactly one bond.
        /// </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="ringCenterVector">A vector pointing the the center of the new ring</param>
        /// <param name="bondLength">The standard bondlength</param>
        public virtual void PlaceFusedRing(IRing ring,
                                           IAtomContainer sharedAtoms,
                                           Vector2 ringCenterVector,
                                           double bondLength)
        {
            Debug.WriteLine("RingPlacer.PlaceFusedRing() start");

            IAtom beg = sharedAtoms.Atoms[0];
            IAtom end = sharedAtoms.Atoms[1];

            Vector2 pBeg = beg.Point2D.Value;
            Vector2 pEnd = end.Point2D.Value;

            // fuse the ring perpendicular to the bond, ring center is not
            // sub-optimal if non-regular/convex polygon (e.g. macro cycle)
            ringCenterVector = GetPerpendicular(pBeg, pEnd, ringCenterVector);

            double radius = GetNativeRingRadius(ring, bondLength);
            double newRingPerpendicular = Math.Sqrt(Math.Pow(radius, 2) - Math.Pow(bondLength / 2, 2));

            ringCenterVector = Vector2.Normalize(ringCenterVector);
            Debug.WriteLine($"placeFusedRing->: ringCenterVector.Length {ringCenterVector.Length()}");
            ringCenterVector *= newRingPerpendicular;
            Vector2 ringCenter = GetMidPoint(pBeg, pEnd);

            ringCenter += ringCenterVector;

            Vector2 originRingCenterVector = ringCenter;

            pBeg -= originRingCenterVector;
            pEnd -= originRingCenterVector;

            double occupiedAngle = Angle(pBeg, pEnd);

            double remainingAngle = (2 * Math.PI) - occupiedAngle;
            double addAngle       = remainingAngle / (ring.RingSize - 1);

            Debug.WriteLine("placeFusedRing->occupiedAngle: " + Vectors.RadianToDegree(occupiedAngle));
            Debug.WriteLine("placeFusedRing->remainingAngle: " + Vectors.RadianToDegree(remainingAngle));
            Debug.WriteLine("placeFusedRing->addAngle: " + Vectors.RadianToDegree(addAngle));

            IAtom startAtom;

            double centerX = ringCenter.X;
            double centerY = ringCenter.Y;

            double xDiff = beg.Point2D.Value.X - end.Point2D.Value.X;
            double yDiff = beg.Point2D.Value.Y - end.Point2D.Value.Y;

            double startAngle;;

            int direction = 1;

            // if bond is vertical
            if (xDiff == 0)
            {
                Debug.WriteLine("placeFusedRing->Bond is vertical");
                //starts with the lower Atom
                if (beg.Point2D.Value.Y > end.Point2D.Value.Y)
                {
                    startAtom = beg;
                }
                else
                {
                    startAtom = end;
                }

                //changes the drawing direction
                if (centerX < beg.Point2D.Value.X)
                {
                    direction = 1;
                }
                else
                {
                    direction = -1;
                }
            }

            // if bond is not vertical
            else
            {
                //starts with the left Atom
                if (beg.Point2D.Value.X > end.Point2D.Value.X)
                {
                    startAtom = beg;
                }
                else
                {
                    startAtom = end;
                }

                //changes the drawing direction
                if (centerY - beg.Point2D.Value.Y > (centerX - beg.Point2D.Value.X) * yDiff / xDiff)
                {
                    direction = 1;
                }
                else
                {
                    direction = -1;
                }
            }
            startAngle = GeometryUtil.GetAngle(startAtom.Point2D.Value.X - ringCenter.X, startAtom.Point2D.Value.Y - ringCenter.Y);

            IAtom currentAtom = startAtom;
            // determine first bond in Ring
            //        int k = 0;
            //        for (k = 0; k < ring.GetElectronContainerCount(); k++) {
            //            if (ring.GetElectronContainer(k) is IBond) break;
            //        }
            IBond currentBond = sharedAtoms.Bonds[0];
            var   atomsToDraw = new List <IAtom>();

            for (int i = 0; i < ring.Bonds.Count - 2; i++)
            {
                currentBond = ring.GetNextBond(currentBond, currentAtom);
                currentAtom = currentBond.GetOther(currentAtom);
                atomsToDraw.Add(currentAtom);
            }
            addAngle = addAngle * direction;

#if DEBUG
            Debug.WriteLine("placeFusedRing->startAngle: " + Vectors.RadianToDegree(startAngle));
            Debug.WriteLine("placeFusedRing->addAngle: " + Vectors.RadianToDegree(addAngle));
            Debug.WriteLine("placeFusedRing->startAtom is: " + (Molecule.Atoms.IndexOf(startAtom) + 1));
            Debug.WriteLine("AtomsToDraw: " + AtomPlacer.ListNumbers(Molecule, atomsToDraw));
#endif

            AtomPlacer.PopulatePolygonCorners(atomsToDraw, ringCenter, startAngle, addAngle, radius);
        }
示例#9
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);
        }
示例#10
0
        private void PlaceBridgedRing(IRing ring, IAtomContainer sharedAtoms, Vector2 sharedAtomsCenter, Vector2 ringCenterVector, double bondLength)
        {
            IAtom[]      bridgeAtoms = GetBridgeAtoms(sharedAtoms);
            IAtom        bondAtom1   = bridgeAtoms[0];
            IAtom        bondAtom2   = bridgeAtoms[1];
            List <IAtom> otherAtoms  = new List <IAtom>();

            foreach (IAtom atom in sharedAtoms.Atoms)
            {
                if (atom != bondAtom1 && atom != bondAtom2)
                {
                    otherAtoms.Add(atom);
                }
            }

            bool snap = ring.GetProperty <bool>(SnapHint, false);

            bool swap = snap ? Det(bondAtom1.Point2D.Value, GeometryUtil.Get2DCenter(otherAtoms), bondAtom2.Point2D.Value) < 0
                                : Det(bondAtom1.Point2D.Value, GeometryUtil.Get2DCenter(otherAtoms), bondAtom2.Point2D.Value) > 0;

            if (swap)
            {
                IAtom tmp = bondAtom1;
                bondAtom1 = bondAtom2;
                bondAtom2 = tmp;
            }

            Vector2 bondAtom1Vector = bondAtom1.Point2D.Value;
            Vector2 bondAtom2Vector = bondAtom2.Point2D.Value;

            Vector2 midPoint = GetMidPoint(bondAtom1Vector, bondAtom2Vector);
            Vector2 ringCenter;
            double  radius = GetNativeRingRadius(ring, bondLength);
            double  offset = 0;

            if (snap)
            {
                ringCenter       = midPoint;
                ringCenterVector = GetPerpendicular(bondAtom1Vector, bondAtom2Vector,
                                                    new Vector2(midPoint.X - sharedAtomsCenter.X, midPoint.Y - sharedAtomsCenter.Y));

                offset = 0;
                foreach (IAtom atom in otherAtoms)
                {
                    double dist = Vector2.Distance(atom.Point2D.Value, midPoint);
                    if (dist > offset)
                    {
                        offset = dist;
                    }
                }
            }
            else
            {
                ringCenter = sharedAtomsCenter;
            }

            Vector2.Normalize(ringCenterVector);
            ringCenterVector = ringCenterVector * (radius - offset);
            ringCenter      += ringCenterVector;

            bondAtom1Vector -= ringCenter;
            bondAtom2Vector -= ringCenter;

            int numUnplaced = ring.RingSize - sharedAtoms.Atoms.Count;

            double dot = bondAtom2Vector.X * bondAtom1Vector.X + bondAtom2Vector.Y * bondAtom1Vector.Y;
            double det = bondAtom2Vector.X * bondAtom1Vector.Y - bondAtom2Vector.Y * bondAtom1Vector.X;

            // theta remain/step
            double tRemain = Math.Atan2(det, dot);

            if (tRemain < 0)
            {
                tRemain = Math.PI + (Math.PI + tRemain);
            }
            double tStep = tRemain / (numUnplaced + 1);

            Debug.WriteLine($"placeBridgedRing->tRemain: {Vectors.RadianToDegree(tRemain)}");
            Debug.WriteLine($"placeBridgedRing->tStep: {Vectors.RadianToDegree(tStep)}");

            double startAngle;

            startAngle = GeometryUtil.GetAngle(bondAtom1.Point2D.Value.X - ringCenter.X, bondAtom1.Point2D.Value.Y - ringCenter.Y);

            IAtom currentAtom = bondAtom1;
            IBond currentBond = sharedAtoms.GetConnectedBonds(currentAtom).First();

            List <IAtom> atoms = new List <IAtom>();

            for (int i = 0; i < ring.Bonds.Count; i++)
            {
                currentBond = ring.GetNextBond(currentBond, currentAtom);
                currentAtom = currentBond.GetOther(currentAtom);
                if (!sharedAtoms.Contains(currentAtom))
                {
                    atoms.Add(currentAtom);
                }
            }

#if DEBUG
            Debug.WriteLine($"{nameof(PlaceBridgedRing)}->atomsToPlace: {AtomPlacer.ListNumbers(Molecule, atoms)}");
            Debug.WriteLine($"{nameof(PlaceBridgedRing)}->startAngle: {Vectors.RadianToDegree(startAngle)}");
            Debug.WriteLine($"{nameof(PlaceBridgedRing)}->tStep: {Vectors.RadianToDegree(tStep)}");
#endif
            AtomPlacer.PopulatePolygonCorners(atoms, ringCenter, startAngle, -tStep, radius);
        }