Beispiel #1
0
        public void LargestGapSouthWest()
        {
            Vector2 vector = VecmathUtil.NewVectorInLargestGap(new[] { new Vector2(0, 1), new Vector2(1, 0) });

            Assert.AreEqual(-0.707, vector.X, 0.01);
            Assert.AreEqual(-0.707, vector.Y, 0.01);
            Assert.AreEqual(1, vector.Length(), 0.01);
        }
Beispiel #2
0
        public void LargestGapEast()
        {
            Vector2 vector = VecmathUtil.NewVectorInLargestGap(new[]
            {
                new Vector2(1, 1), new Vector2(1, -1), new Vector2(-1, -1), new Vector2(-1, 1),
                new Vector2(-1, 0), new Vector2(0, 1), new Vector2(0, -1)
            });

            Assert.AreEqual(1, vector.X, 0.01);
            Assert.AreEqual(0, vector.Y, 0.01);
            Assert.AreEqual(1, vector.Length(), 0.01);
        }
Beispiel #3
0
        /// <summary>
        /// Generate a new annotation vector for an atom using the connected bonds and any other occupied
        /// space (auxiliary vectors). The fall back method is to use the largest available space but
        /// some common cases are handled differently. For example, when the number of bonds is two
        /// the annotation is placed in the acute angle of the bonds (providing there is space). This
        /// improves labelling of atoms saturated rings. When there are three bonds and two are 'plain'
        /// the label is again placed in the acute section of the plain bonds.
        /// </summary>
        /// <param name="atom">the atom having an annotation</param>
        /// <param name="bonds">the bonds connected to the atom</param>
        /// <param name="auxVectors">additional vectors to avoid (filled spaced)</param>
        /// <returns>unit vector along which the annotation should be placed.</returns>
        /// <seealso cref="IsPlainBond(IBond)"/>
        /// <seealso cref="VecmathUtil.NewVectorInLargestGap(IList{Vector2})"/>
        internal static Vector2 NewAtomAnnotationVector(IAtom atom, IEnumerable <IBond> bonds, List <Vector2> auxVectors)
        {
            var vectors = new List <Vector2>();

            foreach (var bond in bonds)
            {
                vectors.Add(VecmathUtil.NewUnitVector(atom, bond));
            }

            if (vectors.Count == 0)
            {
                // no bonds, place below
                if (auxVectors.Count == 0)
                {
                    return(new Vector2(0, -1));
                }
                if (auxVectors.Count == 1)
                {
                    return(VecmathUtil.Negate(auxVectors[0]));
                }
                return(VecmathUtil.NewVectorInLargestGap(auxVectors));
            }
            else if (vectors.Count == 1)
            {
                // 1 bond connected
                // H0, then label simply appears on the opposite side
                if (auxVectors.Count == 0)
                {
                    return(VecmathUtil.Negate(vectors[0]));
                }
                // !H0, then place it in the largest gap
                vectors.AddRange(auxVectors);
                return(VecmathUtil.NewVectorInLargestGap(vectors));
            }
            else if (vectors.Count == 2 && auxVectors.Count == 0)
            {
                // 2 bonds connected to an atom with no hydrogen labels

                // sum the vectors such that the label appears in the acute/nook of the two bonds
                var combined = VecmathUtil.Sum(vectors[0], vectors[1]);

                // shallow angle (< 30 deg) means the label probably won't fit
                if (Vectors.Angle(vectors[0], vectors[1]) < Vectors.DegreeToRadian(65))
                {
                    combined = Vector2.Negate(combined);
                }
                else
                {
                    // flip vector if either bond is a non-single bond or a wedge, this will
                    // place the label in the largest space.
                    // However - when both bonds are wedged (consider a bridging system) to
                    // keep the label in the nook of the wedges
                    var bonds_ = bonds.ToList();
                    if ((!IsPlainBond(bonds_[0]) || !IsPlainBond(bonds_[1])) &&
                        !(IsWedged(bonds_[0]) && IsWedged(bonds_[1])))
                    {
                        combined = Vector2.Negate(combined);
                    }
                }

                combined = Vector2.Normalize(combined);

                // did we divide by 0? whoops - this happens when the bonds are collinear
                if (double.IsNaN(combined.Length()))
                {
                    return(VecmathUtil.NewVectorInLargestGap(vectors));
                }

                return(combined);
            }
            else
            {
                if (vectors.Count == 3 && auxVectors.Count == 0)
                {
                    // 3 bonds connected to an atom with no hydrogen label

                    // the easy and common case is to check when two bonds are plain
                    // (i.e. non-stereo sigma bonds) and use those. This gives good
                    // placement for fused conjugated rings

                    var plainVectors = new List <Vector2>();
                    var wedgeVectors = new List <Vector2>();

                    foreach (var bond in bonds)
                    {
                        if (IsPlainBond(bond))
                        {
                            plainVectors.Add(VecmathUtil.NewUnitVector(atom, bond));
                        }
                        if (IsWedged(bond))
                        {
                            wedgeVectors.Add(VecmathUtil.NewUnitVector(atom, bond));
                        }
                    }

                    if (plainVectors.Count == 2)
                    {
                        return(VecmathUtil.Sum(plainVectors[0], plainVectors[1]));
                    }
                    else if (plainVectors.Count + wedgeVectors.Count == 2)
                    {
                        plainVectors.AddRange(wedgeVectors);
                        return(VecmathUtil.Sum(plainVectors[0], plainVectors[1]));
                    }
                }

                // the default option is to find the largest gap
                if (auxVectors.Count > 0)
                {
                    vectors.AddRange(auxVectors);
                }
                return(VecmathUtil.NewVectorInLargestGap(vectors));
            }
        }