/// <summary>
        /// Advances the world model by one frame.
        /// </summary>
        public void Update()
        {
            // Update nodes.
            lock (_nodeLock) {
                // Update the nodes and determine required tree width.
                double halfWidth = 0;
                foreach (Node node in _nodes)
                {
                    node.Update();
                    halfWidth = Math.Max(Math.Abs(node.Location.X), halfWidth);
                    halfWidth = Math.Max(Math.Abs(node.Location.Y), halfWidth);
                    halfWidth = Math.Max(Math.Abs(node.Location.Z), halfWidth);
                }
                Radius = halfWidth;

                // Build tree for node repulsion.
                Octree tree = new Octree(2.1 * halfWidth);
                foreach (Node node in _nodes)
                {
                    tree.Add(node);
                }

                Threading.Parallel.ForEach(_nodes, node => {
                    // Apply repulsion between nodes.
                    tree.Accelerate(node);

                    // Apply origin attraction of nodes.
                    //Vector originDisplacementUnit = -node.Location.Unit();
                    Vector origin = (node.Group == null) ? Vector.Zero : node.Group.Origin;
                    Vector originDisplacementUnit = Unit(origin - node.Location);
                    double originDistance         = (origin - node.Location).Magnitude();

                    double attractionCofficient = OriginFactor;
                    if (originDistance < OriginWeakDistance)
                    {
                        attractionCofficient *= originDistance / OriginWeakDistance;
                    }

                    // Apply group's attraction factor
                    //attractionCofficient *= (node.Group == null) ? 1 : node.Group.Factor;
                    //node.Acceleration += originDisplacementUnit * attractionCofficient / (originDistance + OriginEpsilon);

                    Vector originAcceleration = originDisplacementUnit * attractionCofficient / (originDistance + OriginEpsilon);

                    if (node.Group != null)
                    {
                        originAcceleration.X *= node.Group.Factor.X;
                        originAcceleration.Y *= node.Group.Factor.Y;
                        originAcceleration.Z *= node.Group.Factor.Z;
                    }

                    node.Acceleration += originAcceleration;

                    // Apply edge spring forces.
                    foreach (Node other in node.Connected)
                    {
                        Vector displacement = node.Location.To(other.Location);
                        //Vector direction = displacement.Unit();
                        Vector direction = Unit(displacement);
                        double distance  = displacement.Magnitude();
                        //double idealLength = EdgeLength + node.Radius + other.Radius;

                        // Set a large mass if it's in different groups
                        double mass        = (node.Group != other.Group) ? 1000 : node.Mass;
                        double idealLength = EdgeLength + Node.GetRadius(mass) + other.Radius;

                        node.Acceleration += direction * EdgeFactor * (distance - idealLength) / mass;
                        //node.Acceleration += direction * EdgeFactor * (distance - idealLength) / node.Mass;
                    }
                });
            }
        }