Exemplo n.º 1
0
        /// <summary>
        ///  Distribute the bonded atoms (neighbours) of an atom such that they fill the
        ///  remaining space around an atom in a geometrically nice way.
        ///  IMPORTANT: This method is not supposed to handle the
        ///  case of one or no place neighbor. In the case of
        ///  one placed neighbor, the chain placement methods
        ///  should be used.
        /// </summary>
        /// <param name="atom">The atom whose partners are to be placed</param>
        /// <param name="placedNeighbours">The atoms which are already placed</param>
        /// <param name="unplacedNeighbours">The partners to be placed</param>
        /// <param name="bondLength">The standard bond length for the newly placed atoms</param>
        /// <param name="sharedAtomsCenter">The 2D centre of the placed atoms</param>
        public void DistributePartners(IAtom atom, IAtomContainer placedNeighbours, Vector2 sharedAtomsCenter, IAtomContainer unplacedNeighbours, double bondLength)
        {
            double occupiedAngle = 0;

            IAtom[] sortedAtoms    = null;
            double  startAngle     = 0.0;
            double  addAngle       = 0.0;
            double  radius         = 0.0;
            double  remainingAngle = 0.0;
            // calculate the direction away from the already placed partners of atom
            var sharedAtomsCenterVector = sharedAtomsCenter;

            var newDirection      = atom.Point2D.Value;
            var occupiedDirection = sharedAtomsCenter;

            occupiedDirection = Vector2.Subtract(occupiedDirection, newDirection);
            // if the placing on the centre atom we get NaNs just give a arbitrary direction the
            // rest works it's self out
            if (Math.Abs(occupiedDirection.Length()) < 0.001)
            {
                occupiedDirection = new Vector2(0, 1);
            }
            Debug.WriteLine("distributePartners->occupiedDirection.Lenght(): " + occupiedDirection.Length());
            var atomsToDraw = new List <IAtom>();

            Debug.WriteLine($"Number of shared atoms: {placedNeighbours.Atoms.Count}");

            // IMPORTANT: This method is not supposed to handle the case of one or
            // no place neighbor. In the case of one placed neighbor, the chain
            // placement methods should be used.
            if (placedNeighbours.Atoms.Count == 1)
            {
                Debug.WriteLine("Only one neighbour...");
                for (int f = 0; f < unplacedNeighbours.Atoms.Count; f++)
                {
                    atomsToDraw.Add(unplacedNeighbours.Atoms[f]);
                }

                addAngle = Math.PI * 2 / (unplacedNeighbours.Atoms.Count + placedNeighbours.Atoms.Count);
                // IMPORTANT: At this point we need a calculation of the start
                // angle. Not done yet.
                IAtom  placedAtom = placedNeighbours.Atoms[0];
                double xDiff      = placedAtom.Point2D.Value.X - atom.Point2D.Value.X;
                double yDiff      = placedAtom.Point2D.Value.Y - atom.Point2D.Value.Y;

                Debug.WriteLine("distributePartners->xdiff: " + Vectors.RadianToDegree(xDiff));
                Debug.WriteLine("distributePartners->ydiff: " + Vectors.RadianToDegree(yDiff));
                startAngle = GeometryUtil.GetAngle(xDiff, yDiff);
                Debug.WriteLine("distributePartners->angle: " + Vectors.RadianToDegree(startAngle));

                PopulatePolygonCorners(atomsToDraw, atom.Point2D.Value, startAngle, addAngle, bondLength);
                return;
            }
            else if (placedNeighbours.Atoms.Count == 0)
            {
                Debug.WriteLine("First atom...");
                for (int f = 0; f < unplacedNeighbours.Atoms.Count; f++)
                {
                    atomsToDraw.Add(unplacedNeighbours.Atoms[f]);
                }

                addAngle = Math.PI * 2.0 / unplacedNeighbours.Atoms.Count;

                // IMPORTANT: At this point we need a calculation of the start
                // angle. Not done yet.
                startAngle = 0.0;
                PopulatePolygonCorners(atomsToDraw, atom.Point2D.Value, startAngle, addAngle, bondLength);
                return;
            }

            if (DoAngleSnap(atom, placedNeighbours))
            {
                int numTerminal = 0;
                foreach (var unplaced in unplacedNeighbours.Atoms)
                {
                    if (Molecule.GetConnectedBonds(unplaced).Count() == 1)
                    {
                        numTerminal++;
                    }
                }

                if (numTerminal == unplacedNeighbours.Atoms.Count)
                {
                    var a     = NewVector(placedNeighbours.Atoms[0].Point2D.Value, atom.Point2D.Value);
                    var b     = NewVector(placedNeighbours.Atoms[1].Point2D.Value, atom.Point2D.Value);
                    var d1    = GeometryUtil.GetAngle(a.X, a.Y);
                    var d2    = GeometryUtil.GetAngle(b.X, b.Y);
                    var sweep = Vectors.Angle(a, b);
                    if (sweep < Math.PI)
                    {
                        sweep = 2 * Math.PI - sweep;
                    }
                    startAngle = d2;
                    if (d1 > d2 && d1 - d2 < Math.PI || d2 - d1 >= Math.PI)
                    {
                        startAngle = d1;
                    }
                    sweep /= (1 + unplacedNeighbours.Atoms.Count);
                    PopulatePolygonCorners(unplacedNeighbours.Atoms,
                                           atom.Point2D.Value, startAngle, sweep, bondLength);

                    MarkPlaced(unplacedNeighbours);
                    return;
                }
                else
                {
                    atom.RemoveProperty(MacroCycleLayout.MACROCYCLE_ATOM_HINT);
                }
            }

            // if the least hindered side of the atom is clearly defined (bondLength / 10 is an arbitrary value that seemed reasonable)
            //newDirection.Sub(sharedAtomsCenterVector);
            sharedAtomsCenterVector -= newDirection;
            newDirection             = sharedAtomsCenterVector;
            newDirection             = Vector2.Normalize(newDirection);
            newDirection             = newDirection * bondLength;
            newDirection             = -newDirection;
            Debug.WriteLine($"distributePartners->newDirection.Lenght(): {newDirection.Length()}");
            var distanceMeasure = atom.Point2D.Value;

            distanceMeasure += newDirection;

            // get the two sharedAtom partners with the smallest distance to the new center
            sortedAtoms = placedNeighbours.Atoms.ToArray();
            GeometryUtil.SortBy2DDistance(sortedAtoms, distanceMeasure);
            var closestPoint1 = sortedAtoms[0].Point2D.Value;
            var closestPoint2 = sortedAtoms[1].Point2D.Value;

            closestPoint1 -= atom.Point2D.Value;
            closestPoint2 -= atom.Point2D.Value;
            occupiedAngle  = Vectors.Angle(closestPoint1, occupiedDirection);
            occupiedAngle += Vectors.Angle(closestPoint2, occupiedDirection);

            var angle1 = GeometryUtil.GetAngle(
                sortedAtoms[0].Point2D.Value.X - atom.Point2D.Value.X,
                sortedAtoms[0].Point2D.Value.Y - atom.Point2D.Value.Y);
            var angle2 = GeometryUtil.GetAngle(
                sortedAtoms[1].Point2D.Value.X - atom.Point2D.Value.X,
                sortedAtoms[1].Point2D.Value.Y - atom.Point2D.Value.Y);
            var angle3 = GeometryUtil.GetAngle(
                distanceMeasure.X - atom.Point2D.Value.X,
                distanceMeasure.Y - atom.Point2D.Value.Y);

            if (debug)
            {
                try
                {
                    Debug.WriteLine($"distributePartners->sortedAtoms[0]: {(Molecule.Atoms.IndexOf(sortedAtoms[0]) + 1)}");
                    Debug.WriteLine($"distributePartners->sortedAtoms[1]: {(Molecule.Atoms.IndexOf(sortedAtoms[1]) + 1)}");
                    Debug.WriteLine($"distributePartners->angle1: {Vectors.RadianToDegree(angle1)}");
                    Debug.WriteLine($"distributePartners->angle2: {Vectors.RadianToDegree(angle2)}");
                }
                catch (Exception exc)
                {
                    Debug.WriteLine(exc);
                }
            }
            IAtom startAtom = null;

            if (angle1 > angle3)
            {
                if (angle1 - angle3 < Math.PI)
                {
                    startAtom = sortedAtoms[1];
                }
                else
                {
                    // 12 o'clock is between the two vectors
                    startAtom = sortedAtoms[0];
                }
            }
            else
            {
                if (angle3 - angle1 < Math.PI)
                {
                    startAtom = sortedAtoms[0];
                }
                else
                {
                    // 12 o'clock is between the two vectors
                    startAtom = sortedAtoms[1];
                }
            }
            remainingAngle = (2 * Math.PI) - occupiedAngle;
            addAngle       = remainingAngle / (unplacedNeighbours.Atoms.Count + 1);
            if (debug)
            {
                try
                {
                    Debug.WriteLine($"distributePartners->startAtom: {(Molecule.Atoms.IndexOf(startAtom) + 1)}");
                    Debug.WriteLine($"distributePartners->remainingAngle: {Vectors.RadianToDegree(remainingAngle)}");
                    Debug.WriteLine($"distributePartners->addAngle: {Vectors.RadianToDegree(addAngle)}");
                    Debug.WriteLine($"distributePartners-> partners.Atoms.Count: {unplacedNeighbours.Atoms.Count}");
                }
                catch (Exception exc)
                {
                    Debug.WriteLine(exc);
                }
            }
            for (int f = 0; f < unplacedNeighbours.Atoms.Count; f++)
            {
                atomsToDraw.Add(unplacedNeighbours.Atoms[f]);
            }
            radius     = bondLength;
            startAngle = GeometryUtil.GetAngle(startAtom.Point2D.Value.X - atom.Point2D.Value.X, startAtom.Point2D.Value.Y - atom.Point2D.Value.Y);
            Debug.WriteLine($"Before check: distributePartners->startAngle: {startAngle}");
            Debug.WriteLine($"After check: distributePartners->startAngle: {startAngle}");
            PopulatePolygonCorners(atomsToDraw, atom.Point2D.Value, startAngle, addAngle, radius);
        }