private Vector2 ForceDirectedRepulsion(GraphVertex vertex, float repulsion) { IEnumerable <GraphEdge> inEdges; _project.DependencyGraph.TryGetInEdges(vertex, out inEdges); List <GraphEdge> edgeList = inEdges.ToList(); Vector2 force = Vector2.zero; foreach (GraphVertex other in _project.DependencyGraph.Vertices) { GraphEdge edge = edgeList.Find(x => (x.Source == other) || (x.Target == other)); if (other == vertex || edge != null) { continue; } Vector2 direction = _simulationData[vertex].position - _simulationData[other].position; float distanceToBounds = direction.magnitude - (vertex.Island.Radius + other.Island.Radius); if (distanceToBounds < 0.0f) { Vector2 constrainedPosition = _simulationData[vertex].position - direction.normalized * distanceToBounds; _simulationData[other] = new VertexPositionData(constrainedPosition, constrainedPosition); distanceToBounds = 0f; } float divisor = distanceToBounds + 0.1f; force += (direction.normalized * repulsion) / (divisor * divisor); } return(force); }
public void BuildForceDirectedLayout(OSGiProject project) { _simulationData = new Dictionary <GraphVertex, VertexPositionData>(); _project = project; // TODO: Refactor float attraction = 10.0f; float spring = 1.0f; float repulsion = 5.0f; float attractToCenter = 0.005f; float friction = 0.105f; float timestep = StepSize; foreach (GraphVertex vertex in project.DependencyGraph.Vertices) { float rr = 20f; float x = (SyncDataStorage.Instance.GetRandomNumber(_random) - 0.5f) * rr; float y = (SyncDataStorage.Instance.GetRandomNumber(_random) - 0.5f) * rr; Vector2 startPos = new Vector2(x, y); VertexPositionData vertexData = new VertexPositionData(startPos, startPos); _simulationData.Add(vertex, vertexData); } int stepCounter = 0; while (stepCounter < SimulationSteps) { foreach (GraphVertex vertex in project.DependencyGraph.Vertices) { Vector2 netForce = Vector2.zero; netForce += ForceDirectedAttraction(vertex, attraction, spring); netForce += ForceDirectedRepulsion(vertex, repulsion); netForce -= ForceDirectedAttractToCenter(vertex, attractToCenter); VerletIntegration(vertex, netForce, friction, timestep); netForce = new Vector2((float)Math.Truncate(netForce.x * 10) / 10, (float)Math.Truncate(netForce.y * 10) / 10); stepCounter++; } } foreach (GraphVertex vertex in project.DependencyGraph.Vertices) { VertexPositionData vertexData = _simulationData[vertex]; Vector3 pos = new Vector3(vertexData.position.x, 0, vertexData.position.y); vertex.Position = pos; } }
private void VerletIntegration(GraphVertex vertex, Vector2 force, float friction, float timestep) { Vector2 posDiff = _simulationData[vertex].position - _simulationData[vertex].oldPosition; Vector2 velocity = posDiff / timestep; Vector2 resistance = velocity * timestep * friction; Vector2 oldPosition = _simulationData[vertex].position; Vector2 newPosition = 2.0f * _simulationData[vertex].position - _simulationData[vertex].oldPosition + timestep * timestep * force; newPosition -= resistance; VertexPositionData vertexData = new VertexPositionData(newPosition, oldPosition); _simulationData[vertex] = vertexData; }
private void computeForceDirectedLayoutThreaded(int simulationSteps, float stepSize) { status = Status.Working; Debug.Log("Starting forcedirected graph layout construction."); //Attract Strength multi float c1 = 10.0f; //"Spring" length for maximal dependency strength float c3 = 1.0f; //Repulsion float c4 = 5.0f; //Attract-To-Center float c5 = 0.005f; //Friction float c6 = 0.105f; //TimeStep float t = stepSize; Dictionary <GraphVertex, VertexPositionData> simulationData = new Dictionary <GraphVertex, VertexPositionData>(); #region init start values foreach (GraphVertex vert in graph.Vertices) { float rr = 20f; Vector2 startPos = new Vector2((float)RNG.NextDouble() * rr, (float)RNG.NextDouble() * rr); VertexPositionData vpd = new VertexPositionData(startPos, startPos); simulationData.Add(vert, vpd); } #endregion int stepCounter = 0; while (stepCounter < simulationSteps) { foreach (GraphVertex thisVert in graph.Vertices) { // total force affecting "thisVert" Vector2 netForce = Vector2.zero; #region Attraction IEnumerable <GraphEdge> outEdges; graph.TryGetOutEdges(thisVert, out outEdges); List <GraphEdge> edgeList = outEdges.ToList(); Vector2 springForce = Vector2.zero; foreach (GraphEdge importEdge in edgeList) { GraphVertex otherVert = importEdge.Target; Vector2 direction = simulationData[otherVert].position - simulationData[thisVert].position; float springEquilibriumLength = (thisVert.getIsland().getRadius() + otherVert.getIsland().getRadius()) + c3 * (project.getMaxImportCount() / importEdge.getWeight()); springForce += c1 * direction.normalized * Mathf.Log((direction.magnitude / springEquilibriumLength)); } IEnumerable <GraphEdge> inEdges; graph.TryGetInEdges(thisVert, out inEdges); edgeList = inEdges.ToList(); /* * foreach (GraphEdge exportEdge in edgeList) * { * GraphVertex otherVert = exportEdge.Source; * Vector2 direction = simulationData[otherVert].position - simulationData[thisVert].position; * * float springEquilibriumLength = (thisVert.getIsland().getRadius() + otherVert.getIsland().getRadius()) + c3 / exportEdge.getWeight(); * * springForce += c1 * direction.normalized * Mathf.Log((direction.magnitude / springEquilibriumLength)); * } */ netForce += springForce; #endregion #region Repulsion foreach (GraphVertex otherVert in graph.Vertices) { if (otherVert == thisVert || (edgeList.Find(x => (x.Source == otherVert) || (x.Target == otherVert))) != null) { continue; } Vector2 direction = simulationData[thisVert].position - simulationData[otherVert].position; float distanceToBounds = direction.magnitude - (thisVert.getIsland().getRadius() + otherVert.getIsland().getRadius()); if (distanceToBounds < 0.0f) { Vector2 constrainedPosition = simulationData[thisVert].position - direction.normalized * distanceToBounds; simulationData[thisVert] = new VertexPositionData(constrainedPosition, constrainedPosition); distanceToBounds = 0f; } netForce += (direction.normalized * c4) / (Mathf.Pow(distanceToBounds + 0.1f, 2f)); } #endregion #region Attract-to-Center netForce -= simulationData[thisVert].position * c5; #endregion #region position computation through Verlet-Integration Vector2 currentVelocity = (simulationData[thisVert].position - simulationData[thisVert].oldPosition) / t; Vector2 resistance = currentVelocity * t * c6; Vector2 newPosition = 2.0f * simulationData[thisVert].position - simulationData[thisVert].oldPosition + t * t * netForce; newPosition -= resistance; Vector2 oldPosition = simulationData[thisVert].position; VertexPositionData vpd = new VertexPositionData(newPosition, oldPosition); simulationData[thisVert] = vpd; #endregion stepCounter++; } } #region assign computed positions to graph vertices foreach (GraphVertex vert in graph.Vertices) { VertexPositionData vpd = simulationData[vert]; Vector3 pos = new Vector3(vpd.position.x, 0, vpd.position.y); vert.setPosition(pos); } #endregion status = Status.Finished; Debug.Log("Forcedirected Graph layout is computed!"); cb(); }