Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

215
LINES

< > BotCompany Repo | #1009847 // CALSpringLayout

JavaX fragment (include)

1  
/**
2  
 * The SpringLayout package represents a visualization of a set of nodes. The
3  
 * SpringLayout, which is initialized with a Graph, assigns X/Y locations to
4  
 * each node. When called <code>relax()</code>, the SpringLayout moves the
5  
 * visualization forward one step.
6  
 *
7  
 * @author Danyel Fisher
8  
 * @author Joshua O'Madadhain
9  
 */
10  
sclass CALSpringLayout {
11  
  double repulsion_range_sq = sqr(0.4/*0.5*//*100*/);
12  
  double repulsionFactor = 0.02/*0.01*/;
13  
  
14  
  double desiredLength = defaultDesiredLength;
15  
  static double defaultDesiredLength = 0.3;
16  
  bool center;
17  
18  
  AutoMap<Circle, SpringVertexData> springVertexData = new(SpringVertexData.class);
19  
  
20  
  CirclesAndLines cal;
21  
    
22  
  *() {}
23  
  *(CirclesAndLines *cal) {}
24  
25  
  /**
26  
   * <p>Sets the stretch parameter for this instance.  This value
27  
   * specifies how much the degrees of an edge's incident vertices
28  
   * should influence how easily the endpoints of that edge
29  
   * can move (that is, that edge's tendency to change its length).</p>
30  
   *
31  
   * <p>The default value is 0.70.  Positive values less than 1 cause
32  
   * high-degree vertices to move less than low-degree vertices, and
33  
   * values > 1 cause high-degree vertices to move more than
34  
   * low-degree vertices.  Negative values will have unpredictable
35  
   * and inconsistent results.</p>
36  
   * @param stretch
37  
   */
38  
  double stretch = 0.70;
39  
40  
  /**
41  
   * Returns the current value for the node repulsion range.
42  
   * @see #setRepulsionRange(int)
43  
   */
44  
  public int getRepulsionRange() {
45  
      return (int)(Math.sqrt(repulsion_range_sq));
46  
  }
47  
48  
  /**
49  
   * Sets the node repulsion range (in drawing area units) for this instance.
50  
   * Outside this range, nodes do not repel each other.  The default value
51  
   * is 100.  Negative values are treated as their positive equivalents.
52  
   * @param range
53  
   */
54  
  public void setRepulsionRange(int range) {
55  
      this.repulsion_range_sq = range * range;
56  
  }
57  
58  
  /**
59  
   * Sets the force multiplier for this instance.  This value is used to
60  
   * specify how strongly an edge "wants" to be its default length
61  
   * (higher values indicate a greater attraction for the default length),
62  
   * which affects how much its endpoints move at each timestep.
63  
   * The default value is 1/3.  A value of 0 turns off any attempt by the
64  
   * layout to cause edges to conform to the default length.  Negative
65  
   * values cause long edges to get longer and short edges to get shorter; use
66  
   * at your own risk.
67  
   */
68  
  double force_multiplier = 1.0 / 3.0;
69  
70  
  /**
71  
   * Relaxation step. Moves all nodes a smidge.
72  
   */
73  
  bool step() {
74  
    deleteKeysNotIn(springVertexData, new HashSet(cal.circles));
75  
    
76  
		for (Circle v : cal.circles) {
77  
			SpringVertexData svd = springVertexData.get(v);
78  
			if (svd == null) continue;
79  
			svd.dx /= 4;
80  
			svd.dy /= 4;
81  
			svd.edgedx = svd.edgedy = 0;
82  
			svd.repulsiondx = svd.repulsiondy = 0;
83  
		}
84  
85  
  	relaxEdges();
86  
  	calculateRepulsion();
87  
  	ret moveNodes();
88  
  }
89  
90  
  protected void relaxEdges() {
91  
  	try {
92  
  		for (Line e : cal.lines) {
93  
  		  Circle v1 = e.a, v2 = e.b;
94  
95  
  			Point2D p1 = transform(v1);
96  
  			Point2D p2 = transform(v2);
97  
  			if(p1 == null || p2 == null) continue;
98  
  			double vx = p1.getX() - p2.getX();
99  
  			double vy = p1.getY() - p2.getY();
100  
  			double len = Math.sqrt(vx * vx + vy * vy);
101  
102  
  			// round from zero, if needed [zero would be Bad.].
103  
  			len = (len == 0) ? .0001 : len;
104  
105  
  			double f = force_multiplier * (desiredLength - len) / len;
106  
107  
  			// XXX? f = f * Math.pow(stretch, (getGraph().degree(v1) + getGraph().degree(v2) - 2));
108  
  			
109  
  			// the actual movement distance 'dx' is the force multiplied by the
110  
  			// distance to go.
111  
  			double dx = f * vx;
112  
  			double dy = f * vy;
113  
  			ifdef CALSpringLayout_debug
114  
    			print("desiredLen=" + desiredLen + ", len=" + len + ", f=" + f + ", dx=" + dx + ", dy=" + dy);
115  
  			endifdef
116  
117  
  			SpringVertexData v1D, v2D;
118  
  			v1D = springVertexData.get(v1);
119  
  			v2D = springVertexData.get(v2);
120  
121  
  			v1D.edgedx += dx;
122  
  			v1D.edgedy += dy;
123  
  			v2D.edgedx += -dx;
124  
  			v2D.edgedy += -dy;
125  
  		}
126  
  	} catch(ConcurrentModificationException cme) {
127  
  		relaxEdges();
128  
  	}
129  
  }
130  
131  
  void calculateRepulsion() {
132  
    for (Circle v : cal.circles) {
133  
      SpringVertexData svd = springVertexData.get(v);
134  
      if(svd == null) continue;
135  
      double dx = 0, dy = 0;
136  
137  
      for (Circle v2 : cal.circles) {
138  
        if (v == v2) continue;
139  
        Point2D p = transform(v);
140  
        Point2D p2 = transform(v2);
141  
        if(p == null || p2 == null) continue;
142  
        double vx = p.getX() - p2.getX();
143  
        double vy = p.getY() - p2.getY();
144  
        double distanceSq = p.distanceSq(p2);
145  
        if (distanceSq == 0) {
146  
            dx += Math.random()*0.01;
147  
            dy += Math.random()*0.01;
148  
        } else if (distanceSq < repulsion_range_sq) {
149  
            double factor = 1;
150  
            dx += factor * vx / distanceSq;
151  
            dy += factor * vy / distanceSq;
152  
        }
153  
        
154  
  			ifdef CALSpringLayout_debug
155  
          print("repulsion: distanceSq=" + distanceSq + ", dx=" + dx + ", dy=" + dy);
156  
        endifdef
157  
      }
158  
      double dlen = dx * dx + dy * dy;
159  
      if (dlen > 0) {
160  
        //dlen = Math.sqrt(dlen) / 2 / repulsionFactor;
161  
        // XXX
162  
        dlen = 1000;
163  
        
164  
        svd.repulsiondx += dx / dlen;
165  
        svd.repulsiondy += dy / dlen;
166  
  			ifdef CALSpringLayout_debug
167  
          print("total repulsion: " + dx/dlen + ", " + dy/dlen);
168  
        endifdef
169  
      }
170  
    }
171  
  }
172  
173  
  // returns true if anything was moved
174  
  bool moveNodes() {
175  
    for (Circle v : cal.circles) {
176  
      SpringVertexData vd = springVertexData.get(v);
177  
      if (vd == null) continue;
178  
      Point2D xyd = transform(v);
179  
180  
      vd.dx += vd.repulsiondx + vd.edgedx;
181  
      vd.dy += vd.repulsiondy + vd.edgedy;
182  
183  
      // keeps nodes from moving any faster than 5 per time unit
184  
      v.x += Math.max(-5, Math.min(5, vd.dx));
185  
      v.y += Math.max(-5, Math.min(5, vd.dy));
186  
      pcallF(cal.onLayoutChange, v);
187  
    }
188  
    
189  
    if (center)
190  
      calCenterStepwise(cal, 0.1);
191  
      
192  
    true; // for now
193  
  }
194  
  
195  
  Point2D transform(Circle v) {
196  
    ret new Point2D.Double(v.x, v.y);
197  
  }
198  
  
199  
  void clear() {
200  
    springVertexData.clear();
201  
  }
202  
203  
  static class SpringVertexData {
204  
    double edgedx;
205  
    double edgedy;
206  
    double repulsiondx;
207  
    double repulsiondy;
208  
209  
    /** movement speed, x */
210  
    protected double dx;
211  
212  
    /** movement speed, y */
213  
    protected double dy;
214  
  }
215  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1009847
Snippet name: CALSpringLayout
Eternal ID of this version: #1009847/38
Text MD5: 9f545163380c37cb43a6ecd3d620605b
Author: stefan
Category: javax / a.i. / gui
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2017-09-14 04:18:08
Source code size: 6584 bytes / 215 lines
Pitched / IR pitched: No / No
Views / Downloads: 535 / 1933
Version history: 37 change(s)
Referenced in: [show references]