rubyvis 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +13 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +37 -0
  6. data/History.txt +6 -0
  7. data/LICENSE.txt +23 -0
  8. data/{README.txt → README.md} +15 -12
  9. data/Rakefile +4 -11
  10. data/lib/rubyvis.rb +1 -1
  11. data/lib/rubyvis/scale/quantitative.rb +14 -18
  12. data/lib/rubyvis/scene/svg_label.rb +1 -1
  13. data/rubyvis.gemspec +21 -0
  14. data/spec/anchor_spec.rb +2 -1
  15. data/spec/line_spec.rb +2 -2
  16. data/spec/scale_linear_datetime_spec.rb +23 -8
  17. data/spec/spec_helper.rb +2 -1
  18. metadata +31 -214
  19. data/.gemtest +0 -0
  20. data/vendor/protovis/protovis-r3.3.js +0 -287
  21. data/vendor/protovis/src/behavior/Behavior.js +0 -32
  22. data/vendor/protovis/src/behavior/Drag.js +0 -112
  23. data/vendor/protovis/src/behavior/Pan.js +0 -110
  24. data/vendor/protovis/src/behavior/Point.js +0 -157
  25. data/vendor/protovis/src/behavior/Resize.js +0 -104
  26. data/vendor/protovis/src/behavior/Select.js +0 -100
  27. data/vendor/protovis/src/behavior/Zoom.js +0 -85
  28. data/vendor/protovis/src/color/Color.js +0 -598
  29. data/vendor/protovis/src/color/Colors.js +0 -135
  30. data/vendor/protovis/src/color/Ramp.js +0 -17
  31. data/vendor/protovis/src/data/Arrays.js +0 -277
  32. data/vendor/protovis/src/data/Dom.js +0 -380
  33. data/vendor/protovis/src/data/Flatten.js +0 -146
  34. data/vendor/protovis/src/data/Histogram.js +0 -120
  35. data/vendor/protovis/src/data/LinearScale.js +0 -54
  36. data/vendor/protovis/src/data/LogScale.js +0 -142
  37. data/vendor/protovis/src/data/Nest.js +0 -257
  38. data/vendor/protovis/src/data/Numbers.js +0 -313
  39. data/vendor/protovis/src/data/Objects.js +0 -78
  40. data/vendor/protovis/src/data/OrdinalScale.js +0 -267
  41. data/vendor/protovis/src/data/QuantileScale.js +0 -180
  42. data/vendor/protovis/src/data/QuantitativeScale.js +0 -440
  43. data/vendor/protovis/src/data/RootScale.js +0 -55
  44. data/vendor/protovis/src/data/Scale.js +0 -86
  45. data/vendor/protovis/src/data/Transform.js +0 -109
  46. data/vendor/protovis/src/data/Tree.js +0 -124
  47. data/vendor/protovis/src/data/Vector.js +0 -118
  48. data/vendor/protovis/src/geo/Geo.js +0 -5
  49. data/vendor/protovis/src/geo/GeoScale.js +0 -307
  50. data/vendor/protovis/src/geo/LatLng.js +0 -23
  51. data/vendor/protovis/src/geo/Projection.js +0 -43
  52. data/vendor/protovis/src/geo/Projections.js +0 -117
  53. data/vendor/protovis/src/lang/Array.js +0 -112
  54. data/vendor/protovis/src/lang/init.js +0 -26
  55. data/vendor/protovis/src/layout/Arc.js +0 -178
  56. data/vendor/protovis/src/layout/Bullet.js +0 -164
  57. data/vendor/protovis/src/layout/Cluster.js +0 -205
  58. data/vendor/protovis/src/layout/Force.js +0 -309
  59. data/vendor/protovis/src/layout/Grid.js +0 -119
  60. data/vendor/protovis/src/layout/Hierarchy.js +0 -249
  61. data/vendor/protovis/src/layout/Horizon.js +0 -159
  62. data/vendor/protovis/src/layout/Indent.js +0 -83
  63. data/vendor/protovis/src/layout/Layout.js +0 -56
  64. data/vendor/protovis/src/layout/Matrix.js +0 -177
  65. data/vendor/protovis/src/layout/Network.js +0 -302
  66. data/vendor/protovis/src/layout/Pack.js +0 -323
  67. data/vendor/protovis/src/layout/Partition.js +0 -203
  68. data/vendor/protovis/src/layout/Rollup.js +0 -203
  69. data/vendor/protovis/src/layout/Stack.js +0 -391
  70. data/vendor/protovis/src/layout/Tree.js +0 -282
  71. data/vendor/protovis/src/layout/Treemap.js +0 -347
  72. data/vendor/protovis/src/mark/Anchor.js +0 -81
  73. data/vendor/protovis/src/mark/Area.js +0 -268
  74. data/vendor/protovis/src/mark/Bar.js +0 -93
  75. data/vendor/protovis/src/mark/Dot.js +0 -212
  76. data/vendor/protovis/src/mark/Ease.js +0 -150
  77. data/vendor/protovis/src/mark/Image.js +0 -154
  78. data/vendor/protovis/src/mark/Label.js +0 -155
  79. data/vendor/protovis/src/mark/Line.js +0 -195
  80. data/vendor/protovis/src/mark/Mark.js +0 -1237
  81. data/vendor/protovis/src/mark/Panel.js +0 -273
  82. data/vendor/protovis/src/mark/Rule.js +0 -143
  83. data/vendor/protovis/src/mark/Transient.js +0 -7
  84. data/vendor/protovis/src/mark/Transition.js +0 -195
  85. data/vendor/protovis/src/mark/Wedge.js +0 -244
  86. data/vendor/protovis/src/physics/BoundConstraint.js +0 -75
  87. data/vendor/protovis/src/physics/ChargeForce.js +0 -184
  88. data/vendor/protovis/src/physics/CollisionConstraint.js +0 -113
  89. data/vendor/protovis/src/physics/Constraint.js +0 -26
  90. data/vendor/protovis/src/physics/DragForce.js +0 -49
  91. data/vendor/protovis/src/physics/Force.js +0 -25
  92. data/vendor/protovis/src/physics/Particle.js +0 -81
  93. data/vendor/protovis/src/physics/PositionConstraint.js +0 -72
  94. data/vendor/protovis/src/physics/Quadtree.js +0 -195
  95. data/vendor/protovis/src/physics/Simulation.js +0 -159
  96. data/vendor/protovis/src/physics/SpringForce.js +0 -141
  97. data/vendor/protovis/src/pv-internals.js +0 -154
  98. data/vendor/protovis/src/pv.js +0 -95
  99. data/vendor/protovis/src/scene/SvgArea.js +0 -172
  100. data/vendor/protovis/src/scene/SvgBar.js +0 -28
  101. data/vendor/protovis/src/scene/SvgCurve.js +0 -354
  102. data/vendor/protovis/src/scene/SvgDot.js +0 -81
  103. data/vendor/protovis/src/scene/SvgImage.js +0 -45
  104. data/vendor/protovis/src/scene/SvgLabel.js +0 -46
  105. data/vendor/protovis/src/scene/SvgLine.js +0 -159
  106. data/vendor/protovis/src/scene/SvgPanel.js +0 -126
  107. data/vendor/protovis/src/scene/SvgRule.js +0 -26
  108. data/vendor/protovis/src/scene/SvgScene.js +0 -185
  109. data/vendor/protovis/src/scene/SvgWedge.js +0 -66
  110. data/vendor/protovis/src/text/DateFormat.js +0 -262
  111. data/vendor/protovis/src/text/Format.js +0 -78
  112. data/vendor/protovis/src/text/NumberFormat.js +0 -227
  113. data/vendor/protovis/src/text/TimeFormat.js +0 -115
@@ -1,164 +0,0 @@
1
- // ranges (bad, satisfactory, good)
2
- // measures (actual, forecast)
3
- // markers (previous, goal)
4
-
5
- /*
6
- * Chart design based on the recommendations of Stephen Few. Implementation
7
- * based on the work of Clint Ivy, Jamie Love, and Jason Davies.
8
- * http://projects.instantcognition.com/protovis/bulletchart/
9
- */
10
-
11
- /**
12
- * Constructs a new, empty bullet layout. Layouts are not typically constructed
13
- * directly; instead, they are added to an existing panel via
14
- * {@link pv.Mark#add}.
15
- *
16
- * @class
17
- * @extends pv.Layout
18
- */
19
- pv.Layout.Bullet = function() {
20
- pv.Layout.call(this);
21
- var that = this,
22
- buildImplied = that.buildImplied,
23
- scale = that.x = pv.Scale.linear(),
24
- orient,
25
- horizontal,
26
- rangeColor,
27
- measureColor,
28
- x;
29
-
30
- /** @private Cache layout state to optimize properties. */
31
- this.buildImplied = function(s) {
32
- buildImplied.call(this, x = s);
33
- orient = s.orient;
34
- horizontal = /^left|right$/.test(orient);
35
- rangeColor = pv.ramp("#bbb", "#eee")
36
- .domain(0, Math.max(1, x.ranges.length - 1));
37
- measureColor = pv.ramp("steelblue", "lightsteelblue")
38
- .domain(0, Math.max(1, x.measures.length - 1));
39
- };
40
-
41
- /**
42
- * The range prototype.
43
- *
44
- * @type pv.Mark
45
- * @name pv.Layout.Bullet.prototype.range
46
- */
47
- (this.range = new pv.Mark())
48
- .data(function() { return x.ranges; })
49
- .reverse(true)
50
- .left(function() { return orient == "left" ? 0 : null; })
51
- .top(function() { return orient == "top" ? 0 : null; })
52
- .right(function() { return orient == "right" ? 0 : null; })
53
- .bottom(function() { return orient == "bottom" ? 0 : null; })
54
- .width(function(d) { return horizontal ? scale(d) : null; })
55
- .height(function(d) { return horizontal ? null : scale(d); })
56
- .fillStyle(function() { return rangeColor(this.index); })
57
- .antialias(false)
58
- .parent = that;
59
-
60
- /**
61
- * The measure prototype.
62
- *
63
- * @type pv.Mark
64
- * @name pv.Layout.Bullet.prototype.measure
65
- */
66
- (this.measure = new pv.Mark())
67
- .extend(this.range)
68
- .data(function() { return x.measures; })
69
- .left(function() { return orient == "left" ? 0 : horizontal ? null : this.parent.width() / 3.25; })
70
- .top(function() { return orient == "top" ? 0 : horizontal ? this.parent.height() / 3.25 : null; })
71
- .right(function() { return orient == "right" ? 0 : horizontal ? null : this.parent.width() / 3.25; })
72
- .bottom(function() { return orient == "bottom" ? 0 : horizontal ? this.parent.height() / 3.25 : null; })
73
- .fillStyle(function() { return measureColor(this.index); })
74
- .parent = that;
75
-
76
- /**
77
- * The marker prototype.
78
- *
79
- * @type pv.Mark
80
- * @name pv.Layout.Bullet.prototype.marker
81
- */
82
- (this.marker = new pv.Mark())
83
- .data(function() { return x.markers; })
84
- .left(function(d) { return orient == "left" ? scale(d) : horizontal ? null : this.parent.width() / 2; })
85
- .top(function(d) { return orient == "top" ? scale(d) : horizontal ? this.parent.height() / 2 : null; })
86
- .right(function(d) { return orient == "right" ? scale(d) : null; })
87
- .bottom(function(d) { return orient == "bottom" ? scale(d) : null; })
88
- .strokeStyle("black")
89
- .shape("bar")
90
- .shapeAngle(function() { return horizontal ? 0 : Math.PI / 2; })
91
- .parent = that;
92
-
93
- (this.tick = new pv.Mark())
94
- .data(function() { return scale.ticks(7); })
95
- .left(function(d) { return orient == "left" ? scale(d) : null; })
96
- .top(function(d) { return orient == "top" ? scale(d) : null; })
97
- .right(function(d) { return orient == "right" ? scale(d) : horizontal ? null : -6; })
98
- .bottom(function(d) { return orient == "bottom" ? scale(d) : horizontal ? -8 : null; })
99
- .height(function() { return horizontal ? 6 : null; })
100
- .width(function() { return horizontal ? null : 6; })
101
- .parent = that;
102
- };
103
-
104
- pv.Layout.Bullet.prototype = pv.extend(pv.Layout)
105
- .property("orient", String) // left, right, top, bottom
106
- .property("ranges")
107
- .property("markers")
108
- .property("measures")
109
- .property("maximum", Number);
110
-
111
- /**
112
- * Default properties for bullet layouts.
113
- *
114
- * @type pv.Layout.Bullet
115
- */
116
- pv.Layout.Bullet.prototype.defaults = new pv.Layout.Bullet()
117
- .extend(pv.Layout.prototype.defaults)
118
- .orient("left")
119
- .ranges([])
120
- .markers([])
121
- .measures([]);
122
-
123
- /**
124
- * The orientation.
125
- *
126
- * @type string
127
- * @name pv.Layout.Bullet.prototype.orient
128
- */
129
-
130
- /**
131
- * The array of range values.
132
- *
133
- * @type array
134
- * @name pv.Layout.Bullet.prototype.ranges
135
- */
136
-
137
- /**
138
- * The array of marker values.
139
- *
140
- * @type array
141
- * @name pv.Layout.Bullet.prototype.markers
142
- */
143
-
144
- /**
145
- * The array of measure values.
146
- *
147
- * @type array
148
- * @name pv.Layout.Bullet.prototype.measures
149
- */
150
-
151
- /**
152
- * Optional; the maximum range value.
153
- *
154
- * @type number
155
- * @name pv.Layout.Bullet.prototype.maximum
156
- */
157
-
158
- /** @private */
159
- pv.Layout.Bullet.prototype.buildImplied = function(s) {
160
- pv.Layout.prototype.buildImplied.call(this, s);
161
- var size = this.parent[/^left|right$/.test(s.orient) ? "width" : "height"]();
162
- s.maximum = s.maximum || pv.max([].concat(s.ranges, s.markers, s.measures));
163
- this.x.domain(0, s.maximum).range(0, size);
164
- };
@@ -1,205 +0,0 @@
1
- /**
2
- * Constructs a new, empty cluster layout. Layouts are not typically
3
- * constructed directly; instead, they are added to an existing panel via
4
- * {@link pv.Mark#add}.
5
- *
6
- * @class Implements a hierarchical layout using the cluster (or dendrogram)
7
- * algorithm. This layout provides both node-link and space-filling
8
- * implementations of cluster diagrams. In many ways it is similar to
9
- * {@link pv.Layout.Partition}, except that leaf nodes are positioned at maximum
10
- * depth, and the depth of internal nodes is based on their distance from their
11
- * deepest descendant, rather than their distance from the root.
12
- *
13
- * <p>The cluster layout supports a "group" property, which if true causes
14
- * siblings to be positioned closer together than unrelated nodes at the same
15
- * depth. Unlike the partition layout, this layout does not support dynamic
16
- * sizing for leaf nodes; all leaf nodes are the same size.
17
- *
18
- * <p>For more details on how to use this layout, see
19
- * {@link pv.Layout.Hierarchy}.
20
- *
21
- * @see pv.Layout.Cluster.Fill
22
- * @extends pv.Layout.Hierarchy
23
- */
24
- pv.Layout.Cluster = function() {
25
- pv.Layout.Hierarchy.call(this);
26
- var interpolate, // cached interpolate
27
- buildImplied = this.buildImplied;
28
-
29
- /** @private Cache layout state to optimize properties. */
30
- this.buildImplied = function(s) {
31
- buildImplied.call(this, s);
32
- interpolate
33
- = /^(top|bottom)$/.test(s.orient) ? "step-before"
34
- : /^(left|right)$/.test(s.orient) ? "step-after"
35
- : "linear";
36
- };
37
-
38
- this.link.interpolate(function() { return interpolate; });
39
- };
40
-
41
- pv.Layout.Cluster.prototype = pv.extend(pv.Layout.Hierarchy)
42
- .property("group", Number)
43
- .property("orient", String)
44
- .property("innerRadius", Number)
45
- .property("outerRadius", Number);
46
-
47
- /**
48
- * The group parameter; defaults to 0, disabling grouping of siblings. If this
49
- * parameter is set to a positive number (or true, which is equivalent to 1),
50
- * then additional space will be allotted between sibling groups. In other
51
- * words, siblings (nodes that share the same parent) will be positioned more
52
- * closely than nodes at the same depth that do not share a parent.
53
- *
54
- * @type number
55
- * @name pv.Layout.Cluster.prototype.group
56
- */
57
-
58
- /**
59
- * The orientation. The default orientation is "top", which means that the root
60
- * node is placed on the top edge, leaf nodes appear on the bottom edge, and
61
- * internal nodes are in-between. The following orientations are supported:<ul>
62
- *
63
- * <li>left - left-to-right.
64
- * <li>right - right-to-left.
65
- * <li>top - top-to-bottom.
66
- * <li>bottom - bottom-to-top.
67
- * <li>radial - radially, with the root at the center.</ul>
68
- *
69
- * @type string
70
- * @name pv.Layout.Cluster.prototype.orient
71
- */
72
-
73
- /**
74
- * The inner radius; defaults to 0. This property applies only to radial
75
- * orientations, and can be used to compress the layout radially. Note that for
76
- * the node-link implementation, the root node is always at the center,
77
- * regardless of the value of this property; this property only affects internal
78
- * and leaf nodes. For the space-filling implementation, a non-zero value of
79
- * this property will result in the root node represented as a ring rather than
80
- * a circle.
81
- *
82
- * @type number
83
- * @name pv.Layout.Cluster.prototype.innerRadius
84
- */
85
-
86
- /**
87
- * The outer radius; defaults to fill the containing panel, based on the height
88
- * and width of the layout. If the layout has no height and width specified, it
89
- * will extend to fill the enclosing panel.
90
- *
91
- * @type number
92
- * @name pv.Layout.Cluster.prototype.outerRadius
93
- */
94
-
95
- /**
96
- * Defaults for cluster layouts. The default group parameter is 0 and the
97
- * default orientation is "top".
98
- *
99
- * @type pv.Layout.Cluster
100
- */
101
- pv.Layout.Cluster.prototype.defaults = new pv.Layout.Cluster()
102
- .extend(pv.Layout.Hierarchy.prototype.defaults)
103
- .group(0)
104
- .orient("top");
105
-
106
- /** @private */
107
- pv.Layout.Cluster.prototype.buildImplied = function(s) {
108
- if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return;
109
-
110
- var root = s.nodes[0],
111
- group = s.group,
112
- breadth,
113
- depth,
114
- leafCount = 0,
115
- leafIndex = .5 - group / 2;
116
-
117
- /* Count the leaf nodes and compute the depth of descendants. */
118
- var p = undefined;
119
- root.visitAfter(function(n) {
120
- if (n.firstChild) {
121
- n.depth = 1 + pv.max(n.childNodes, function(n) { return n.depth; });
122
- } else {
123
- if (group && (p != n.parentNode)) {
124
- p = n.parentNode;
125
- leafCount += group;
126
- }
127
- leafCount++;
128
- n.depth = 0;
129
- }
130
- });
131
- breadth = 1 / leafCount;
132
- depth = 1 / root.depth;
133
-
134
- /* Compute the unit breadth and depth of each node. */
135
- var p = undefined;
136
- root.visitAfter(function(n) {
137
- if (n.firstChild) {
138
- n.breadth = pv.mean(n.childNodes, function(n) { return n.breadth; });
139
- } else {
140
- if (group && (p != n.parentNode)) {
141
- p = n.parentNode;
142
- leafIndex += group;
143
- }
144
- n.breadth = breadth * leafIndex++;
145
- }
146
- n.depth = 1 - n.depth * depth;
147
- });
148
-
149
- /* Compute breadth and depth ranges for space-filling layouts. */
150
- root.visitAfter(function(n) {
151
- n.minBreadth = n.firstChild
152
- ? n.firstChild.minBreadth
153
- : (n.breadth - breadth / 2);
154
- n.maxBreadth = n.firstChild
155
- ? n.lastChild.maxBreadth
156
- : (n.breadth + breadth / 2);
157
- });
158
- root.visitBefore(function(n) {
159
- n.minDepth = n.parentNode
160
- ? n.parentNode.maxDepth
161
- : 0;
162
- n.maxDepth = n.parentNode
163
- ? (n.depth + root.depth)
164
- : (n.minDepth + 2 * root.depth);
165
- });
166
- root.minDepth = -depth;
167
-
168
- pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s);
169
- };
170
-
171
- /**
172
- * Constructs a new, empty space-filling cluster layout. Layouts are not
173
- * typically constructed directly; instead, they are added to an existing panel
174
- * via {@link pv.Mark#add}.
175
- *
176
- * @class A variant of cluster layout that is space-filling. The meaning of the
177
- * exported mark prototypes changes slightly in the space-filling
178
- * implementation:<ul>
179
- *
180
- * <li><tt>node</tt> - for rendering nodes; typically a {@link pv.Bar} for
181
- * non-radial orientations, and a {@link pv.Wedge} for radial orientations.
182
- *
183
- * <p><li><tt>link</tt> - unsupported; undefined. Links are encoded implicitly
184
- * in the arrangement of the space-filling nodes.
185
- *
186
- * <p><li><tt>label</tt> - for rendering node labels; typically a
187
- * {@link pv.Label}.
188
- *
189
- * </ul>For more details on how to use this layout, see
190
- * {@link pv.Layout.Cluster}.
191
- *
192
- * @extends pv.Layout.Cluster
193
- */
194
- pv.Layout.Cluster.Fill = function() {
195
- pv.Layout.Cluster.call(this);
196
- pv.Layout.Hierarchy.Fill.constructor.call(this);
197
- };
198
-
199
- pv.Layout.Cluster.Fill.prototype = pv.extend(pv.Layout.Cluster);
200
-
201
- /** @private */
202
- pv.Layout.Cluster.Fill.prototype.buildImplied = function(s) {
203
- if (pv.Layout.Cluster.prototype.buildImplied.call(this, s)) return;
204
- pv.Layout.Hierarchy.Fill.buildImplied.call(this, s);
205
- };
@@ -1,309 +0,0 @@
1
- /**
2
- * Constructs a new, empty force-directed layout. Layouts are not typically
3
- * constructed directly; instead, they are added to an existing panel via
4
- * {@link pv.Mark#add}.
5
- *
6
- * @class Implements force-directed network layout as a node-link diagram. This
7
- * layout uses the Fruchterman-Reingold algorithm, which applies an attractive
8
- * spring force between neighboring nodes, and a repulsive electrical charge
9
- * force between all nodes. An additional drag force improves stability of the
10
- * simulation. See {@link pv.Force.spring}, {@link pv.Force.drag} and {@link
11
- * pv.Force.charge} for more details; note that the n-body charge force is
12
- * approximated using the Barnes-Hut algorithm.
13
- *
14
- * <p>This layout is implemented on top of {@link pv.Simulation}, which can be
15
- * used directly for more control over simulation parameters. The simulation
16
- * uses Position Verlet integration, which does not compute velocities
17
- * explicitly, but allows for easy geometric constraints, such as bounding the
18
- * nodes within the layout panel. Many of the configuration properties supported
19
- * by this layout are simply passed through to the underlying forces and
20
- * constraints of the simulation.
21
- *
22
- * <p>Force layouts are typically interactive. The gradual movement of the nodes
23
- * as they stabilize to a local stress minimum can help reveal the structure of
24
- * the network, as can {@link pv.Behavior.drag}, which allows the user to pick
25
- * up nodes and reposition them while the physics simulation continues. This
26
- * layout can also be used with pan &amp; zoom behaviors for interaction.
27
- *
28
- * <p>To facilitate interaction, this layout by default automatically re-renders
29
- * using a <tt>setInterval</tt> every 42 milliseconds. This can be disabled via
30
- * the <tt>iterations</tt> property, which if non-null specifies the number of
31
- * simulation iterations to run before the force-directed layout is finalized.
32
- * Be careful not to use too high an iteration count, as this can lead to an
33
- * annoying delay on page load.
34
- *
35
- * <p>As with other network layouts, the network data can be updated
36
- * dynamically, provided the property cache is reset. See
37
- * {@link pv.Layout.Network} for details. New nodes are initialized with random
38
- * positions near the center. Alternatively, positions can be specified manually
39
- * by setting the <tt>x</tt> and <tt>y</tt> attributes on nodes.
40
- *
41
- * @extends pv.Layout.Network
42
- * @see <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.13.8444&rep=rep1&type=pdf"
43
- * >"Graph Drawing by Force-directed Placement"</a> by T. Fruchterman &amp;
44
- * E. Reingold, Software--Practice &amp; Experience, November 1991.
45
- */
46
- pv.Layout.Force = function() {
47
- pv.Layout.Network.call(this);
48
-
49
- /* Force-directed graphs can be messy, so reduce the link width. */
50
- this.link.lineWidth(function(d, p) { return Math.sqrt(p.linkValue) * 1.5; });
51
- this.label.textAlign("center");
52
- };
53
-
54
- pv.Layout.Force.prototype = pv.extend(pv.Layout.Network)
55
- .property("bound", Boolean)
56
- .property("iterations", Number)
57
- .property("dragConstant", Number)
58
- .property("chargeConstant", Number)
59
- .property("chargeMinDistance", Number)
60
- .property("chargeMaxDistance", Number)
61
- .property("chargeTheta", Number)
62
- .property("springConstant", Number)
63
- .property("springDamping", Number)
64
- .property("springLength", Number);
65
-
66
- /**
67
- * The bound parameter; true if nodes should be constrained within the layout
68
- * panel. Bounding is disabled by default. Currently the layout does not observe
69
- * the radius of the nodes; strictly speaking, only the center of the node is
70
- * constrained to be within the panel, with an additional 6-pixel offset for
71
- * padding. A future enhancement could extend the bound constraint to observe
72
- * the node's radius, which would also support bounding for variable-size nodes.
73
- *
74
- * <p>Note that if this layout is used in conjunction with pan &amp; zoom
75
- * behaviors, those behaviors should have their bound parameter set to the same
76
- * value.
77
- *
78
- * @type boolean
79
- * @name pv.Layout.Force.prototype.bound
80
- */
81
-
82
- /**
83
- * The number of simulation iterations to run, or null if this layout is
84
- * interactive. Force-directed layouts are interactive by default, using a
85
- * <tt>setInterval</tt> to advance the physics simulation and re-render
86
- * automatically.
87
- *
88
- * @type number
89
- * @name pv.Layout.Force.prototype.iterations
90
- */
91
-
92
- /**
93
- * The drag constant, in the range [0,1]. A value of 0 means no drag (a
94
- * perfectly frictionless environment), while a value of 1 means friction
95
- * immediately cancels all momentum. The default value is 0.1, which provides a
96
- * minimum amount of drag that helps stabilize bouncy springs; lower values may
97
- * result in excessive bounciness, while higher values cause the simulation to
98
- * take longer to converge.
99
- *
100
- * @type number
101
- * @name pv.Layout.Force.prototype.dragConstant
102
- * @see pv.Force.drag#constant
103
- */
104
-
105
- /**
106
- * The charge constant, which should be a negative number. The default value is
107
- * -40; more negative values will result in a stronger repulsive force, which
108
- * may lead to faster convergence at the risk of instability. Too strong
109
- * repulsive charge forces can cause comparatively weak springs to be stretched
110
- * well beyond their rest length, emphasizing global structure over local
111
- * structure. A nonnegative value will break the Fruchterman-Reingold algorithm,
112
- * and is for entertainment purposes only.
113
- *
114
- * @type number
115
- * @name pv.Layout.Force.prototype.chargeConstant
116
- * @see pv.Force.charge#constant
117
- */
118
-
119
- /**
120
- * The minimum distance at which charge forces are applied. The default minimum
121
- * distance of 2 avoids applying forces that are two strong; because the physics
122
- * simulation is run at discrete time intervals, it is possible for two same-
123
- * charged particles to become very close or even a singularity! Since the
124
- * charge force is inversely proportional to the square of the distance, very
125
- * small distances can break the simulation.
126
- *
127
- * <p>In rare cases, two particles can become stuck on top of each other, as a
128
- * minimum distance threshold will prevent the charge force from repelling them.
129
- * However, this occurs very rarely because other forces and momentum typically
130
- * cause the particles to become separated again, at which point the repulsive
131
- * charge force kicks in.
132
- *
133
- * @type number
134
- * @name pv.Layout.Force.prototype.chargeMinDistance
135
- * @see pv.Force.charge#domain
136
- */
137
-
138
- /**
139
- * The maximum distance at which charge forces are applied. This improves
140
- * performance by ignoring weak charge forces at great distances. Note that this
141
- * parameter is partly redundant, as the Barnes-Hut algorithm for n-body forces
142
- * already improves performance for far-away particles through approximation.
143
- *
144
- * @type number
145
- * @name pv.Layout.Force.prototype.chargeMaxDistance
146
- * @see pv.Force.charge#domain
147
- */
148
-
149
- /**
150
- * The Barnes-Hut approximation factor. The Barnes-Hut approximation criterion
151
- * is the ratio of the size of the quadtree node to the distance from the point
152
- * to the node's center of mass is beneath some threshold. The default value is
153
- * 0.9.
154
- *
155
- * @type number
156
- * @name pv.Layout.Force.prototype.chargeTheta
157
- * @see pv.Force.charge#theta
158
- */
159
-
160
- /**
161
- * The spring constant, which should be a positive number. The default value is
162
- * 0.1; greater values will result in a stronger attractive force, which may
163
- * lead to faster convergence at the risk of instability. Too strong spring
164
- * forces can cause comparatively weak charge forces to be ignored, emphasizing
165
- * local structure over global structure. A nonpositive value will break the
166
- * Fruchterman-Reingold algorithm, and is for entertainment purposes only.
167
- *
168
- * <p>The spring tension is automatically normalized using the inverse square
169
- * root of the maximum link degree of attached nodes.
170
- *
171
- * @type number
172
- * @name pv.Layout.Force.prototype.springConstant
173
- * @see pv.Force.spring#constant
174
- */
175
-
176
- /**
177
- * The spring damping factor, in the range [0,1]. Damping functions identically
178
- * to drag forces, damping spring bounciness by applying a force in the opposite
179
- * direction of attached nodes' velocities. The default value is 0.3.
180
- *
181
- * <p>The spring damping is automatically normalized using the inverse square
182
- * root of the maximum link degree of attached nodes.
183
- *
184
- * @type number
185
- * @name pv.Layout.Force.prototype.springDamping
186
- * @see pv.Force.spring#damping
187
- */
188
-
189
- /**
190
- * The spring rest length. The default value is 20 pixels. Larger values may be
191
- * appropriate if the layout panel is larger, or if the nodes are rendered
192
- * larger than the default dot size of 20.
193
- *
194
- * @type number
195
- * @name pv.Layout.Force.prototype.springLength
196
- * @see pv.Force.spring#length
197
- */
198
-
199
- /**
200
- * Default properties for force-directed layouts. The default drag constant is
201
- * 0.1, the default charge constant is -40 (with a domain of [2, 500] and theta
202
- * of 0.9), and the default spring constant is 0.1 (with a damping of 0.3 and a
203
- * rest length of 20).
204
- *
205
- * @type pv.Layout.Force
206
- */
207
- pv.Layout.Force.prototype.defaults = new pv.Layout.Force()
208
- .extend(pv.Layout.Network.prototype.defaults)
209
- .dragConstant(.1)
210
- .chargeConstant(-40)
211
- .chargeMinDistance(2)
212
- .chargeMaxDistance(500)
213
- .chargeTheta(.9)
214
- .springConstant(.1)
215
- .springDamping(.3)
216
- .springLength(20);
217
-
218
- /** @private Initialize the physics simulation. */
219
- pv.Layout.Force.prototype.buildImplied = function(s) {
220
-
221
- /* Any cached interactive layouts need to be rebound for the timer. */
222
- if (pv.Layout.Network.prototype.buildImplied.call(this, s)) {
223
- var f = s.$force;
224
- if (f) {
225
- f.next = this.binds.$force;
226
- this.binds.$force = f;
227
- }
228
- return;
229
- }
230
-
231
- var that = this,
232
- nodes = s.nodes,
233
- links = s.links,
234
- k = s.iterations,
235
- w = s.width,
236
- h = s.height;
237
-
238
- /* Initialize positions randomly near the center. */
239
- for (var i = 0, n; i < nodes.length; i++) {
240
- n = nodes[i];
241
- if (isNaN(n.x)) n.x = w / 2 + 40 * Math.random() - 20;
242
- if (isNaN(n.y)) n.y = h / 2 + 40 * Math.random() - 20;
243
- }
244
-
245
- /* Initialize the simulation. */
246
- var sim = pv.simulation(nodes);
247
-
248
- /* Drag force. */
249
- sim.force(pv.Force.drag(s.dragConstant));
250
-
251
- /* Charge (repelling) force. */
252
- sim.force(pv.Force.charge(s.chargeConstant)
253
- .domain(s.chargeMinDistance, s.chargeMaxDistance)
254
- .theta(s.chargeTheta));
255
-
256
- /* Spring (attracting) force. */
257
- sim.force(pv.Force.spring(s.springConstant)
258
- .damping(s.springDamping)
259
- .length(s.springLength)
260
- .links(links));
261
-
262
- /* Position constraint (for interactive dragging). */
263
- sim.constraint(pv.Constraint.position());
264
-
265
- /* Optionally add bound constraint. TODO: better padding. */
266
- if (s.bound) {
267
- sim.constraint(pv.Constraint.bound().x(6, w - 6).y(6, h - 6));
268
- }
269
-
270
- /** @private Returns the speed of the given node, to determine cooling. */
271
- function speed(n) {
272
- return n.fix ? 1 : n.vx * n.vx + n.vy * n.vy;
273
- }
274
-
275
- /*
276
- * If the iterations property is null (the default), the layout is
277
- * interactive. The simulation is run until the fastest particle drops below
278
- * an arbitrary minimum speed. Although the timer keeps firing, this speed
279
- * calculation is fast so there is minimal CPU overhead. Note: if a particle
280
- * is fixed for interactivity, treat this as a high speed and resume
281
- * simulation.
282
- */
283
- if (k == null) {
284
- sim.step(); // compute initial previous velocities
285
- sim.step(); // compute initial velocities
286
-
287
- /* Add the simulation state to the bound list. */
288
- var force = s.$force = this.binds.$force = {
289
- next: this.binds.$force,
290
- nodes: nodes,
291
- min: 1e-4 * (links.length + 1),
292
- sim: sim
293
- };
294
-
295
- /* Start the timer, if not already started. */
296
- if (!this.$timer) this.$timer = setInterval(function() {
297
- var render = false;
298
- for (var f = that.binds.$force; f; f = f.next) {
299
- if (pv.max(f.nodes, speed) > f.min) {
300
- f.sim.step();
301
- render = true;
302
- }
303
- }
304
- if (render) that.render();
305
- }, 42);
306
- } else for (var i = 0; i < k; i++) {
307
- sim.step();
308
- }
309
- };