Network Diagram layout algorithm
In order to realize the automatic layout of the pre-trend diagram in the course of writing, we refer to the famous Network Diagram software Gephi, and decide to use the FR algorithm to automatically layout the nodes.
Basic idea of algorithm
The FR algorithm considers all nodes to be electrons, and each node receives two forces: 1. Coulomb Force (repulsion) of other nodes 2. Edge-to-point Hukeli (Gravity). Then under the interaction of force, the entire layout will eventually be called a state of equilibrium.
The algorithm sets the repulsion force and attraction to
? \ (f_a (d) = \frac{d^2}{k}\) \ (F_r (d) = \frac{-k^2}{d}\)
The best distance formula for the distance d between the two atoms is defined as follows
? \[k = C\sqrt{\frac{area}{number of Vectices}} \]
where \ (area = w*l\) in order to ensure that the midpoint coordinates in multiple iterations will not displace out of bounds, C uses simulated annealing to set a temperature to prevent displacement out of bounds.
In my actual time temperature set an initial value, I took the \ (temperture = w/10\)
Algorithm pseudo-code:
area:= W? L {W and L are the width and length of the frame} G: = (V, E); {The vertices is assigned random initial positions} K: = parea/| V |; function fa (x): = begin return x2/k end; function fr (x): = begin return k2/x end; For I: =1To iterations does begin {calculate repulsive forces} for the V does begin {each vertex have both vectors:. Pos and. Disp V.disp: =0; For u in V do if (U6= v) THEN BEGIN {Δis The difference vector between the positions of the vertices}δ:= V.pos? U.pos; V.disp: = V.disp + (δ/|δ|)? Fr (|δ|) End END {calculate attractive forces} for E in E does begin {Each edges are an ordere D pair of vertices. vand.u}δ:= e.v.pos? E.u.pos; E.v.disp: = E.v.disp? (δ/|δ|) ? FA (|δ|); E.u.disp: = E.u.disp + (δ/|δ|)? FA (|δ|) End {Limit Max displacement to temperature T and prevent from displacement outside frame} for V-v do Begin V.pos: = V.pos + (v.disp/|v.disp|)? Min (V.disp, t); V.pos.x: = Min (w/2, Max (? w/2, v.pos.x)); V.pos.y: = min (L/2, Max (? L2, V.pos.y)) end {Reduce the temperature as the layout approaches a better configuration} t: = C Ool (t) end
My Java implementation
Package Main.model;import Java.util.HashMap;import java.util.List;import Java.util.Map;/*** @Author dyleaf* @Description: The auto-layout algorithm use Fruchterman and Reingold model* @Date: 20:23 2018/2/25 */ Public classFruchtermanreingoldlayout {Private intW//width of canvas Private intL//Length of canvas Private intTemperature = W/Ten;//Simulated annealing initial temperature Private intMaxiter = +;//algorithm iteration count Private intArea = W * L;//Layout size Private DoubleC =1;//node Distance control factor Private DoubleKdistance between//nodes /*** Init fruchtermanreingoldlayout * @param WThe wide of graph * @param LThe length of graph * @param maxiterThe Max iterator of the Arig * @param Ratedefine the initial value of temperature */ Public void Init(intWintLintMaxiter,intRateDoubleC) { This.W= W; This.L= L; This.Maxiter= Maxiter; temperature = W/rate; This.C= C; } PublicList<node>Run(list<node> nodes, list<edge> edges) {list<node> resetnodes = nodes; for(inti =0; i < Maxiter; i++) {resetnodes =Springlayout(Resetnodes, edges, i); }returnResetnodes; } PublicList<node>Springlayout(list<node> nodes, list<edge> edges,intCuriter) {//2 calculates the unit displacement (usually positive) resulting from repulsion between 22 nodes in a local area of each iteration. DoubleDeltaX, DeltaY, deltalength; k= c* Math.sqrt(Area/(Double) nodes.size()); map<string, double> dispx =NewHashmap<string, double> (); map<string, double> dispy =NewHashmap<string, double> (); for(intv =0; V < nodes.size(); v++) {dispx.put(Nodes.Get(v).getId(),0.0); Dispy.put(Nodes.Get(v).getId(),0.0); for(intU =0; U < nodes.size(); u++) {if(U! = V) {DeltaX = nodes.Get(v).GetX()-Nodes.Get(u).GetX();if(Double.IsNaN(DeltaX)) {System. out.println("x Error"+ nodes.Get(v).GetX()); } DeltaY = nodes.Get(v).GetY()-Nodes.Get(u).GetY();if(Double.IsNaN(DeltaY)) {System. out.println("Y error"+ nodes.Get(v).GetX()); } deltalength = Math.sqrt(DeltaX * deltax + deltay * deltay);Doubleforce = k * K/DELTALENGTH;if(Double.IsNaN(Force)) {System.Err.println("Force was NaN node is"+ U +" ,"+ V +"Diflength"+ Deltalength +"x"+ DeltaX +"Y"+ DeltaY); The String id = nodes.Get(v).getId(); Dispx.put(ID, dispx.Get(ID) + (deltax/deltalength) * force); Dispy.put(ID, Dispy.Get(ID) + (deltay/deltalength) * force); } } }//3. Calculates the unit displacement (generally negative) generated by the gravitational force of each edge on each iteration of the two-way nodeNode visnodes =NULL, Visnodee =NULL; for(intE =0; e < edges.size(); e++) {String Estartid = edges.Get(e).Getsourceid(); String Eendid = edges.Get(e).Getendid(); Visnodes =Getnodebyid(nodes, Estartid); Visnodee =Getnodebyid(nodes, Eendid); DeltaX = Visnodes.GetX()-Visnodee.GetX(); DeltaY = Visnodes.GetY()-Visnodee.GetY(); Deltalength = Math.sqrt(DeltaX * deltax + deltay * deltay);Doubleforce = deltalength * DELTALENGTH/K;if(Double.IsNaN(Force)) {System.Err.println("Force was NaN edge is"+ Visnodes.ID+" ,"+ Visnodee.ID); }DoubleXdisp = (deltax/deltalength) * Force;DoubleYdisp = (deltay/deltalength) * Force; Dispx.put(Estartid, dispx.Get(Estartid)-xdisp); Dispy.put(Estartid, Dispy.Get(Estartid)-ydisp); Dispx.put(Eendid, dispx.Get(Eendid) + xdisp); Dispy.put(Eendid, Dispy.Get(Eendid) + ydisp); }//set x, y for(intv =0; V < nodes.size(); v++) {Node node = nodes.Get(v); Double dx = dispx.Get(node.getId()); Double dy = dispy.Get(node.getId()); Double displength = Math.sqrt(DX * dx + dy * dy);DoubleXdisp = Dx/displength * Math.min(displength, temperature);DoubleYdisp = Dy/displength * Math.min(displength, temperature);//don ' t let nodes leave the displayNode.SetX(node.GetX() +xdisp); Node.sety(node.GetY() +ydisp); Node.SetX(Math.minW2, Math.Max(-1.0W2, node.GetX()))); Node.sety(Math.minL2, Math.Max(-1.0L2, node.GetY()))); }//cool Temperature Cool(Curiter);//temperature*=0.95; returnNodes }Private void Cool(intCuriter) {Temperature *= (1.0-Curiter/(Double) maxiter); }PrivateNodeGetnodebyid(list<node> nodes, String ID) { for(Node node:nodes) {if(node.getId().equals(ID)) {returnNode } }return NULL; }}
Reference
force-directed Drawing Algorithms
Fr Algorithm (Fruchterman-reingold)