/// <summary> /// Adds a node to the appropriate subtree based on its position in space. /// </summary> /// <param name="node">The node to add to a subtree.</param> private void AddToSubtree(Node node) { double subtreeWidth = _width / 2; // Don't create subtrees if it violates the width limit. if (subtreeWidth >= MinimumWidth) { if (_subtrees == null) { _subtrees = new Octree[8]; } // Determine which subtree the node belongs in and add it to that subtree. int subtreeIndex = 0; for (int i = -1; i <= 1; i += 2) { for (int j = -1; j <= 1; j += 2) { for (int k = -1; k <= 1; k += 2) { Vector subtreeLocation = _location + (subtreeWidth / 2) * new Vector(i, j, k); // Determine if the node is contained within the bounds of the subtree under // consideration. if (Math.Abs(subtreeLocation.X - node.Location.X) <= subtreeWidth / 2 && Math.Abs(subtreeLocation.Y - node.Location.Y) <= subtreeWidth / 2 && Math.Abs(subtreeLocation.Z - node.Location.Z) <= subtreeWidth / 2) { if (_subtrees[subtreeIndex] == null) { _subtrees[subtreeIndex] = new Octree(subtreeLocation, subtreeWidth); } _subtrees[subtreeIndex].Add(node); return; } subtreeIndex++; } } } } }
/// <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); } // Build tree for node repulsion. Octree tree = new Octree(2.1 * halfWidth); foreach (Node node in _nodes) { tree.Add(node); } if (_nodes.Count > 0) { Parallel.ForEach(_nodes, node => { // Apply repulsion between nodes. tree.Accelerate(node); // Apply origin attraction of nodes. Vector originDisplacementUnit = -node.Location.Unit(); double originDistance = node.Location.Magnitude(); double attractionCofficient = OriginFactor; if (originDistance < OriginWeakDistance) { attractionCofficient *= originDistance / OriginWeakDistance; } node.Acceleration += originDisplacementUnit * attractionCofficient / (originDistance + OriginEpsilon); // Apply edge spring forces. foreach (Node other in node.Connected) { Vector displacement = node.Location.To(other.Location); Vector direction = displacement.Unit(); double distance = displacement.Magnitude(); double idealLength = EdgeLength + node.Radius + other.Radius; node.Acceleration += direction * EdgeFactor * (distance - idealLength) / node.Mass; } }); } // Update frame info. if (_nodes.Count > 0) { Frames++; } } // Update camera. _cameraZ += _cameraZVelocity * _cameraZ; _cameraZ = Math.Max(1, _cameraZ); _cameraZVelocity *= CameraZEasing; _renderer.Camera.Z = _cameraZ; }
/// <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; } }); } }