InitializeMetadata
        (
            ICollection <IVertex> verticesToLayOut
        )
        {
            Debug.Assert(verticesToLayOut != null);
            AssertValid();

            foreach (IVertex oVertex in verticesToLayOut)
            {
                // Create an object that will store all calculated values for the
                // vertex.

                FruchtermanReingoldVertexInfo oFruchtermanReingoldVertexInfo =
                    new FruchtermanReingoldVertexInfo(oVertex.Location);

                // The object could be stored in a metadata key, but because the
                // number of retrievals can be very large, it's more efficient to
                // store it in the Tag.  If a Tag already exists, save it in a
                // metadata key.

                Object oTag = oVertex.Tag;

                if (oTag != null)
                {
                    oVertex.SetValue(
                        ReservedMetadataKeys.FruchtermanReingoldLayoutTagStorage,
                        oTag
                        );
                }

                oVertex.Tag = oFruchtermanReingoldVertexInfo;
            }
        }
        SetUnboundedLocations
        (
            ICollection <IVertex> verticesToLayOut,
            LayoutContext layoutContext,
            Single fTemperature,
            Boolean bAlsoSetVertexLocations
        )
        {
            Debug.Assert(verticesToLayOut != null);
            Debug.Assert(layoutContext != null);
            Debug.Assert(fTemperature > 0);
            AssertValid();

            // The following variables define the unbounded rectangle.

            TMathType tMinLocationX = Single.MaxValue;
            TMathType tMaxLocationX = Single.MinValue;

            TMathType tMinLocationY = Single.MaxValue;
            TMathType tMaxLocationY = Single.MinValue;

            foreach (IVertex oVertex in verticesToLayOut)
            {
                // Retrieve the object that stores calculated values for the
                // vertex.  We need the vertex's current unbounded location and
                // the displacement created by the repulsive and attractive forces
                // on the vertex.

                FruchtermanReingoldVertexInfo oVertexInfo =
                    (FruchtermanReingoldVertexInfo)oVertex.Tag;

                TMathType tUnboundedLocationX =
                    (TMathType)oVertexInfo.UnboundedLocationX;

                TMathType tUnboundedLocationY =
                    (TMathType)oVertexInfo.UnboundedLocationY;

                TMathType tDisplacementX = (TMathType)oVertexInfo.DisplacementX;
                TMathType tDisplacementY = (TMathType)oVertexInfo.DisplacementY;

                TMathType tDisplacement = (TMathType)Math.Sqrt(
                    (tDisplacementX * tDisplacementX) +
                    (tDisplacementY * tDisplacementY)
                    );

                if (tDisplacement != 0)
                {
                    // Calculate a new unbounded location, limited by the current
                    // temperature.

                    tUnboundedLocationX += (tDisplacementX / tDisplacement) *
                                           Math.Min(tDisplacement, (TMathType)fTemperature);

                    tUnboundedLocationY += (tDisplacementY / tDisplacement) *
                                           Math.Min(tDisplacement, (TMathType)fTemperature);
                }

                // Update the vertex's unbounded location.

                oVertexInfo.UnboundedLocationX = (Single)tUnboundedLocationX;
                oVertexInfo.UnboundedLocationY = (Single)tUnboundedLocationY;

                // Expand the unbounded rectangle if necessary.

                tMinLocationX = Math.Min(tUnboundedLocationX, tMinLocationX);
                tMaxLocationX = Math.Max(tUnboundedLocationX, tMaxLocationX);

                tMinLocationY = Math.Min(tUnboundedLocationY, tMinLocationY);
                tMaxLocationY = Math.Max(tUnboundedLocationY, tMaxLocationY);
            }

            if (!bAlsoSetVertexLocations)
            {
                return;
            }

            Debug.Assert(verticesToLayOut.Count != 0);

            Debug.Assert(tMinLocationX != Single.MaxValue);
            Debug.Assert(tMaxLocationX != Single.MinValue);
            Debug.Assert(tMinLocationY != Single.MaxValue);
            Debug.Assert(tMaxLocationY != Single.MinValue);

            // Get a Matrix that will transform vertex locations from coordinates
            // in the unbounded rectangle to cooordinates in the bounded graph
            // rectangle.

            Matrix oTransformationMatrix = LayoutUtil.GetRectangleTransformation(

                RectangleF.FromLTRB(
                    (Single)tMinLocationX,
                    (Single)tMinLocationY,
                    (Single)tMaxLocationX,
                    (Single)tMaxLocationY
                    ),

                layoutContext.GraphRectangle
                );

            // Transform the vertex locations.

            foreach (IVertex oVertex in verticesToLayOut)
            {
                FruchtermanReingoldVertexInfo oVertexInfo =
                    (FruchtermanReingoldVertexInfo)oVertex.Tag;

                PointF [] aoLocation = new PointF [] {
                    new PointF(
                        oVertexInfo.UnboundedLocationX,
                        oVertexInfo.UnboundedLocationY
                        )
                };

                oTransformationMatrix.TransformPoints(aoLocation);

                if (!VertexIsLocked(oVertex))
                {
                    oVertex.Location = aoLocation[0];
                }
            }
        }
        CalculateAttractiveForces
        (
            ICollection <IEdge> edgesToLayOut,
            Single k
        )
        {
            Debug.Assert(edgesToLayOut != null);
            Debug.Assert(k != 0);
            AssertValid();

            const String MethodName = "CalculateAttractiveForces";

            foreach (IEdge oEdge in edgesToLayOut)
            {
                if (oEdge.IsSelfLoop)
                {
                    // A vertex isn't attracted to itself.

                    continue;
                }

                // Get the edge's vertices.

                IVertex oVertexV, oVertexU;

                EdgeUtil.EdgeToVertices(oEdge, this.ClassName, MethodName,
                                        out oVertexV, out oVertexU);

                // Retrieve the objects that store calculated values for the
                // vertices.

                FruchtermanReingoldVertexInfo oVertexInfoV =
                    (FruchtermanReingoldVertexInfo)oVertexV.Tag;

                FruchtermanReingoldVertexInfo oVertexInfoU =
                    (FruchtermanReingoldVertexInfo)oVertexU.Tag;

                TMathType tDeltaX =
                    (TMathType)oVertexInfoV.UnboundedLocationX -
                    (TMathType)oVertexInfoU.UnboundedLocationX;

                TMathType tDeltaY =
                    (TMathType)oVertexInfoV.UnboundedLocationY -
                    (TMathType)oVertexInfoU.UnboundedLocationY;

                TMathType tDelta = (TMathType)Math.Sqrt(
                    (tDeltaX * tDeltaX) + (tDeltaY * tDeltaY)
                    );

                TMathType tDisplacementV_X = (TMathType)oVertexInfoV.DisplacementX;
                TMathType tDisplacementV_Y = (TMathType)oVertexInfoV.DisplacementY;

                TMathType tDisplacementU_X = (TMathType)oVertexInfoU.DisplacementX;
                TMathType tDisplacementU_Y = (TMathType)oVertexInfoU.DisplacementY;

                // (Note that there is an obvious typo in the Fruchterman-Reingold
                // paper for computing the attractive force.  The function fa(z) at
                // the top of Figure 1 is defined as x squared over k.  It should
                // read z squared over k.)

                TMathType fa = (tDelta * tDelta) / (TMathType)k;

                if (tDelta == 0)
                {
                    // TODO: Is this the correct way to handle vertices in the same
                    // location?  See the notes in CalculateRepulsiveForces().

                    continue;
                }

                Debug.Assert(tDelta != 0);

                TMathType faOverDelta = fa / tDelta;

                TMathType tFactorX = tDeltaX * faOverDelta;
                TMathType tFactorY = tDeltaY * faOverDelta;

                tDisplacementV_X -= tFactorX;
                tDisplacementV_Y -= tFactorY;

                tDisplacementU_X += tFactorX;
                tDisplacementU_Y += tFactorY;

                oVertexInfoV.DisplacementX = (Single)tDisplacementV_X;
                oVertexInfoV.DisplacementY = (Single)tDisplacementV_Y;

                oVertexInfoU.DisplacementX = (Single)tDisplacementU_X;
                oVertexInfoU.DisplacementY = (Single)tDisplacementU_Y;
            }
        }
        CalculateRepulsiveForces
        (
            ICollection <IVertex> verticesToLayOut,
            Single k
        )
        {
            Debug.Assert(verticesToLayOut != null);
            AssertValid();

            TMathType tkSquared = (TMathType)(k * k);

            foreach (IVertex oVertexV in verticesToLayOut)
            {
                // Retrieve the object that stores calculated values for the
                // vertex.

                FruchtermanReingoldVertexInfo oVertexInfoV =
                    (FruchtermanReingoldVertexInfo)oVertexV.Tag;

                TMathType tDisplacementX = 0;
                TMathType tDisplacementY = 0;

                foreach (IVertex oVertexU in verticesToLayOut)
                {
                    if (oVertexU == oVertexV)
                    {
                        continue;
                    }

                    FruchtermanReingoldVertexInfo oVertexInfoU =
                        (FruchtermanReingoldVertexInfo)oVertexU.Tag;

                    TMathType tDeltaX =
                        (TMathType)oVertexInfoV.UnboundedLocationX -
                        (TMathType)oVertexInfoU.UnboundedLocationX;

                    TMathType tDeltaY =
                        (TMathType)oVertexInfoV.UnboundedLocationY -
                        (TMathType)oVertexInfoU.UnboundedLocationY;

                    TMathType tDelta = (TMathType)Math.Sqrt(
                        (tDeltaX * tDeltaX) + (tDeltaY * tDeltaY)
                        );

                    // The Fruchterman-Reingold paper says this about vertices in
                    // the same location:
                    //
                    // "A special case occurs when vertices are in the same
                    // position: our implementation acts as though the two vertices
                    // are a small distance apart in a randomly chosen orientation:
                    // this leads to a violent repulsive effect separating them."
                    //
                    // Handle this case by arbitrarily setting a small
                    // displacement.

                    if (tDelta == 0)
                    {
                        tDisplacementX += 1;
                        tDisplacementY += 1;
                    }
                    else
                    {
                        Debug.Assert(tDelta != 0);

                        TMathType fr = tkSquared / tDelta;

                        TMathType frOverDelta = fr / tDelta;

                        tDisplacementX += tDeltaX * frOverDelta;
                        tDisplacementY += tDeltaY * frOverDelta;
                    }
                }

                // Save the results for VertexV.

                oVertexInfoV.DisplacementX = tDisplacementX;
                oVertexInfoV.DisplacementY = tDisplacementY;
            }
        }
        //*************************************************************************
        //  Method: InitializeMetadata()
        //
        /// <summary>
        /// Stores required metadata on the graph's vertices before the layout
        /// begins.
        /// </summary>
        ///
        /// <param name="verticesToLayOut">
        /// Vertices to lay out.  The collection is guaranteed to have at least one
        /// vertex.
        /// </param>
        //*************************************************************************
        protected void InitializeMetadata(
            ICollection<IVertex> verticesToLayOut
            )
        {
            Debug.Assert(verticesToLayOut != null);
            AssertValid();

            foreach (IVertex oVertex in verticesToLayOut)
            {
            // Create an object that will store all calculated values for the
            // vertex.

            FruchtermanReingoldVertexInfo oFruchtermanReingoldVertexInfo =
                new FruchtermanReingoldVertexInfo(oVertex.Location);

            // The object could be stored in a metadata key, but because the
            // number of retrievals can be very large, it's more efficient to
            // store it in the Tag.  If a Tag already exists, save it in a
            // metadata key.

            Object oTag = oVertex.Tag;

            if (oTag != null)
            {
                oVertex.SetValue(
                    ReservedMetadataKeys.FruchtermanReingoldLayoutTagStorage,
                    oTag
                    );
            }

            oVertex.Tag = oFruchtermanReingoldVertexInfo;
            }
        }