示例#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);
        }
示例#2
0
        public virtual void TestGetNextBond_IBond_IAtom()
        {
            IRing ring = (IRing)NewChemObject();
            IAtom c1   = ring.Builder.NewAtom("C");
            IAtom c2   = ring.Builder.NewAtom("C");
            IAtom c3   = ring.Builder.NewAtom("C");
            IBond b1   = ring.Builder.NewBond(c1, c2, BondOrder.Single);
            IBond b2   = ring.Builder.NewBond(c3, c2, BondOrder.Single);
            IBond b3   = ring.Builder.NewBond(c1, c3, BondOrder.Single);

            ring.Atoms.Add(c1);
            ring.Atoms.Add(c2);
            ring.Atoms.Add(c3);
            ring.Bonds.Add(b1);
            ring.Bonds.Add(b2);
            ring.Bonds.Add(b3);

            Assert.AreEqual(b1, ring.GetNextBond(b2, c2));
            Assert.AreEqual(b1, ring.GetNextBond(b3, c1));
            Assert.AreEqual(b2, ring.GetNextBond(b1, c2));
            Assert.AreEqual(b2, ring.GetNextBond(b3, c3));
            Assert.AreEqual(b3, ring.GetNextBond(b1, c1));
            Assert.AreEqual(b3, ring.GetNextBond(b2, c3));
        }
示例#3
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);
        }
示例#4
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);
        }
示例#5
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);
        }