d3-cloud-rails 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c2a9280e5f2a9b7b97904fcd16263498912e58aa
4
+ data.tar.gz: f21841cfe1989f5120aa9ca235d3a914f52ab337
5
+ SHA512:
6
+ metadata.gz: cb2cd746d4c19a4a98136553c5b1ee6d4412fa519cf750d799c72947f60ef8590749537fa651dd8b649004025fe3b7f5ba776f2748605b3d81c8bcd5ddf12607
7
+ data.tar.gz: bf2e4580c694a5ed3ef2be6d75b085cad4d2a408609f4b2575e219c66e3ebebe89c65a6abc0586bdbc81bcf88624aa359e1a0962a77b75f8b1e581bb67fe4c19
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Anthony Hernandez
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ # D3::Cloud::Rails
2
+ This is a Rails plugin for generating wordle-like word clouds using D3.js. The work is based almost entirely on that done by jasondavies.
3
+ You can find the original javascript project along with the API [here](https://github.com/jasondavies/d3-cloud).
4
+
5
+
6
+ ## Usage
7
+ You must have D3.js installed to use this gem. The author recommends [d3-rails](https://github.com/iblue/d3-rails).
8
+
9
+ In `app/assets/javascripts/application.js`:
10
+
11
+ ```js
12
+ //= require d3
13
+ //= require d3-cloud
14
+ ```
15
+
16
+ ```js
17
+ $(function(){
18
+ var frequency_list = [{"text":"study","size":40},{"text":"motion","size":15},{"text":"forces","size":10},{"text":"electricity","size":15},{"text":"movement","size":10},{"text":"relation","size":5},{"text":"things","size":10},{"text":"force","size":5},{"text":"ad","size":5},{"text":"energy","size":85},{"text":"living","size":5},{"text":"nonliving","size":5},{"text":"laws","size":15},{"text":"speed","size":45},{"text":"velocity","size":30},{"text":"define","size":5},{"text":"constraints","size":5},{"text":"universe","size":10},{"text":"physics","size":120},{"text":"describing","size":5},{"text":"matter","size":90},{"text":"physics-the","size":5},{"text":"world","size":10},{"text":"works","size":10},{"text":"science","size":70},{"text":"interactions","size":30},{"text":"studies","size":5},{"text":"properties","size":45},{"text":"nature","size":40},{"text":"branch","size":30},{"text":"concerned","size":25},{"text":"source","size":40},{"text":"google","size":10},{"text":"defintions","size":5},{"text":"two","size":15},{"text":"grouped","size":15},{"text":"traditional","size":15},{"text":"fields","size":15},{"text":"acoustics","size":15},{"text":"optics","size":15},{"text":"mechanics","size":20},{"text":"thermodynamics","size":15},{"text":"electromagnetism","size":15},{"text":"modern","size":15},{"text":"extensions","size":15},{"text":"thefreedictionary","size":15},{"text":"interaction","size":15},{"text":"org","size":25},{"text":"answers","size":5},{"text":"natural","size":15},{"text":"objects","size":5},{"text":"treats","size":10},{"text":"acting","size":5},{"text":"department","size":5},{"text":"gravitation","size":5},{"text":"heat","size":10},{"text":"light","size":10},{"text":"magnetism","size":10},{"text":"modify","size":5},{"text":"general","size":10},{"text":"bodies","size":5},{"text":"philosophy","size":5},{"text":"brainyquote","size":5},{"text":"words","size":5},{"text":"ph","size":5},{"text":"html","size":5},{"text":"lrl","size":5},{"text":"zgzmeylfwuy","size":5},{"text":"subject","size":5},{"text":"distinguished","size":5},{"text":"chemistry","size":5},{"text":"biology","size":5},{"text":"includes","size":5},{"text":"radiation","size":5},{"text":"sound","size":5},{"text":"structure","size":5},{"text":"atoms","size":5},{"text":"including","size":10},{"text":"atomic","size":10},{"text":"nuclear","size":10},{"text":"cryogenics","size":10},{"text":"solid-state","size":10},{"text":"particle","size":10},{"text":"plasma","size":10},{"text":"deals","size":5},{"text":"merriam-webster","size":5},{"text":"dictionary","size":10},{"text":"analysis","size":5},{"text":"conducted","size":5},{"text":"order","size":5},{"text":"understand","size":5},{"text":"behaves","size":5},{"text":"en","size":5},{"text":"wikipedia","size":5},{"text":"wiki","size":5},{"text":"physics-","size":5},{"text":"physical","size":5},{"text":"behaviour","size":5},{"text":"collinsdictionary","size":5},{"text":"english","size":5},{"text":"time","size":35},{"text":"distance","size":35},{"text":"wheels","size":5},{"text":"revelations","size":5},{"text":"minute","size":5},{"text":"acceleration","size":20},{"text":"torque","size":5},{"text":"wheel","size":5},{"text":"rotations","size":5},{"text":"resistance","size":5},{"text":"momentum","size":5},{"text":"measure","size":10},{"text":"direction","size":10},{"text":"car","size":5},{"text":"add","size":5},{"text":"traveled","size":5},{"text":"weight","size":5},{"text":"electrical","size":5},{"text":"power","size":5}];
19
+
20
+
21
+ var color = d3.scaleSqrt()
22
+ .domain([0,1,2,3,4,5,6,10,15,20,100])
23
+ .range(["#ddd", "#ccc", "#bbb", "#aaa", "#999", "#888", "#777", "#666", "#555", "#444", "#333", "#222"]);
24
+
25
+ d3.cloud.size([800, 300])
26
+ .words(frequency_list)
27
+ .fontSize(function(d) { return d.size; })
28
+ .on("end", draw)
29
+ .start();
30
+
31
+ function draw(words) {
32
+ d3.select("body").append("svg")
33
+ .attr("width", 850)
34
+ .attr("height", 350)
35
+ .attr("class", "wordcloud")
36
+ .append("g")
37
+ // without the transform, words words would get cutoff to the left and top, they would
38
+ // appear outside of the SVG area
39
+ .attr("transform", "translate(320,200)")
40
+ .selectAll("text")
41
+ .data(words)
42
+ .enter().append("text")
43
+ .style("font-size", function(d) { return d.size + "px"; })
44
+ .style("fill", function(d, i) { return color(i); })
45
+ .attr("transform", function(d) {
46
+ return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
47
+ })
48
+ .text(function(d) { return d.text; });
49
+ }
50
+ });
51
+
52
+ ```
53
+
54
+ ![Example usage with code below](http://i68.tinypic.com/w9wh2q.png)
55
+
56
+ ## Installation
57
+ Add this line to your application's Gemfile:
58
+
59
+ ```ruby
60
+ gem 'd3-cloud-rails'
61
+ ```
62
+
63
+ And then execute:
64
+ ```bash
65
+ $ bundle
66
+ ```
67
+
68
+ Or install it yourself as:
69
+ ```bash
70
+ $ gem install d3-cloud-rails
71
+ ```
72
+
73
+ ## Contributing
74
+ Please feel free to help maintain this gem. Simply fork the repo and add any necessary changes / updates, and then open a PR stating what you did and why.
75
+
76
+ ## License
77
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,19 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'D3::Cloud::Rails'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ load 'rails/tasks/statistics.rake'
18
+
19
+ require 'bundler/gem_tasks'
@@ -0,0 +1,392 @@
1
+ var cloudRadians = Math.PI / 180, cw = 1 << 11 >> 5, ch = 1 << 11;
2
+ $(function() {
3
+ var size = [256, 256],
4
+ text = cloudText,
5
+ font = cloudFont,
6
+ fontSize = cloudFontSize,
7
+ fontStyle = cloudFontNormal,
8
+ fontWeight = cloudFontNormal,
9
+ rotate = cloudRotate,
10
+ padding = cloudPadding,
11
+ spiral = archimedeanSpiral,
12
+ words = [],
13
+ timeInterval = Infinity,
14
+ event = d3.dispatch("word", "end"),
15
+ timer = null,
16
+ random = Math.random,
17
+ cloud = {},
18
+ canvas = cloudCanvas;
19
+
20
+ cloud.canvas = function(_) {
21
+ return arguments.length ? (canvas = functor(_), cloud) : canvas;
22
+ };
23
+
24
+ cloud.start = function() {
25
+ var contextAndRatio = getContext(canvas()),
26
+ board = zeroArray((size[0] >> 5) * size[1]),
27
+ bounds = null,
28
+ n = words.length,
29
+ i = -1,
30
+ tags = [],
31
+ data = words.map(function(d, i) {
32
+ d.text = text.call(this, d, i);
33
+ d.font = font.call(this, d, i);
34
+ d.style = fontStyle.call(this, d, i);
35
+ d.weight = fontWeight.call(this, d, i);
36
+ d.rotate = rotate.call(this, d, i);
37
+ d.size = ~~fontSize.call(this, d, i);
38
+ d.padding = padding.call(this, d, i);
39
+ return d;
40
+ }).sort(function(a, b) { return b.size - a.size; });
41
+
42
+ if (timer) clearInterval(timer);
43
+ timer = setInterval(step, 0);
44
+ step();
45
+
46
+ return cloud;
47
+
48
+ function step() {
49
+ var start = Date.now();
50
+ while (Date.now() - start < timeInterval && ++i < n && timer) {
51
+ var d = data[i];
52
+ d.x = (size[0] * (random() + .5)) >> 1;
53
+ d.y = (size[1] * (random() + .5)) >> 1;
54
+ cloudSprite(contextAndRatio, d, data, i);
55
+ if (d.hasText && place(board, d, bounds)) {
56
+ tags.push(d);
57
+ event.call("word", cloud, d);
58
+ if (bounds) cloudBounds(bounds, d);
59
+ else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}];
60
+ // Temporary hack
61
+ d.x -= size[0] >> 1;
62
+ d.y -= size[1] >> 1;
63
+ }
64
+ }
65
+ if (i >= n) {
66
+ cloud.stop();
67
+ event.call("end", cloud, tags, bounds);
68
+ }
69
+ }
70
+ }
71
+
72
+ cloud.stop = function() {
73
+ if (timer) {
74
+ clearInterval(timer);
75
+ timer = null;
76
+ }
77
+ return cloud;
78
+ };
79
+
80
+ function getContext(canvas) {
81
+ canvas.width = canvas.height = 1;
82
+ var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
83
+ canvas.width = (cw << 5) / ratio;
84
+ canvas.height = ch / ratio;
85
+
86
+ var context = canvas.getContext("2d");
87
+ context.fillStyle = context.strokeStyle = "red";
88
+ context.textAlign = "center";
89
+
90
+ return {context: context, ratio: ratio};
91
+ }
92
+
93
+ function place(board, tag, bounds) {
94
+ var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}],
95
+ startX = tag.x,
96
+ startY = tag.y,
97
+ maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]),
98
+ s = spiral(size),
99
+ dt = random() < .5 ? 1 : -1,
100
+ t = -dt,
101
+ dxdy,
102
+ dx,
103
+ dy;
104
+
105
+ while (dxdy = s(t += dt)) {
106
+ dx = ~~dxdy[0];
107
+ dy = ~~dxdy[1];
108
+
109
+ if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break;
110
+
111
+ tag.x = startX + dx;
112
+ tag.y = startY + dy;
113
+
114
+ if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 ||
115
+ tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue;
116
+ // TODO only check for collisions within current bounds.
117
+ if (!bounds || !cloudCollide(tag, board, size[0])) {
118
+ if (!bounds || collideRects(tag, bounds)) {
119
+ var sprite = tag.sprite,
120
+ w = tag.width >> 5,
121
+ sw = size[0] >> 5,
122
+ lx = tag.x - (w << 4),
123
+ sx = lx & 0x7f,
124
+ msx = 32 - sx,
125
+ h = tag.y1 - tag.y0,
126
+ x = (tag.y + tag.y0) * sw + (lx >> 5),
127
+ last;
128
+ for (var j = 0; j < h; j++) {
129
+ last = 0;
130
+ for (var i = 0; i <= w; i++) {
131
+ board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
132
+ }
133
+ x += sw;
134
+ }
135
+ delete tag.sprite;
136
+ return true;
137
+ }
138
+ }
139
+ }
140
+ return false;
141
+ }
142
+
143
+ cloud.timeInterval = function(_) {
144
+ return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval;
145
+ };
146
+
147
+ cloud.words = function(_) {
148
+ return arguments.length ? (words = _, cloud) : words;
149
+ };
150
+
151
+ cloud.size = function(_) {
152
+ return arguments.length ? (size = [+_[0], +_[1]], cloud) : size;
153
+ };
154
+
155
+ cloud.font = function(_) {
156
+ return arguments.length ? (font = functor(_), cloud) : font;
157
+ };
158
+
159
+ cloud.fontStyle = function(_) {
160
+ return arguments.length ? (fontStyle = functor(_), cloud) : fontStyle;
161
+ };
162
+
163
+ cloud.fontWeight = function(_) {
164
+ return arguments.length ? (fontWeight = functor(_), cloud) : fontWeight;
165
+ };
166
+
167
+ cloud.rotate = function(_) {
168
+ return arguments.length ? (rotate = functor(_), cloud) : rotate;
169
+ };
170
+
171
+ cloud.text = function(_) {
172
+ return arguments.length ? (text = functor(_), cloud) : text;
173
+ };
174
+
175
+ cloud.spiral = function(_) {
176
+ return arguments.length ? (spiral = spirals[_] || _, cloud) : spiral;
177
+ };
178
+
179
+ cloud.fontSize = function(_) {
180
+ return arguments.length ? (fontSize = functor(_), cloud) : fontSize;
181
+ };
182
+
183
+ cloud.padding = function(_) {
184
+ return arguments.length ? (padding = functor(_), cloud) : padding;
185
+ };
186
+
187
+ cloud.random = function(_) {
188
+ return arguments.length ? (random = _, cloud) : random;
189
+ };
190
+
191
+ cloud.on = function() {
192
+ var value = event.on.apply(event, arguments);
193
+ return value === event ? cloud : value;
194
+ };
195
+
196
+ function cloudText(d) {
197
+ return d.text;
198
+ }
199
+
200
+ function cloudFont() {
201
+ return "serif";
202
+ }
203
+
204
+ function cloudFontNormal() {
205
+ return "normal";
206
+ }
207
+
208
+ function cloudFontSize(d) {
209
+ return Math.sqrt(d.value);
210
+ }
211
+
212
+ function cloudRotate() {
213
+ return (~~(Math.random() * 6) - 3) * 30;
214
+ }
215
+
216
+ function cloudPadding() {
217
+ return 1;
218
+ }
219
+
220
+ // Fetches a monochrome sprite bitmap for the specified text.
221
+ // Load in batches for speed.
222
+ function cloudSprite(contextAndRatio, d, data, di) {
223
+ if (d.sprite) return;
224
+ var c = contextAndRatio.context,
225
+ ratio = contextAndRatio.ratio;
226
+
227
+ c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
228
+ var x = 0,
229
+ y = 0,
230
+ maxh = 0,
231
+ n = data.length;
232
+ --di;
233
+ while (++di < n) {
234
+ d = data[di];
235
+ c.save();
236
+ c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font;
237
+ var w = c.measureText(d.text + "m").width * ratio,
238
+ h = d.size << 1;
239
+ if (d.rotate) {
240
+ var sr = Math.sin(d.rotate * cloudRadians),
241
+ cr = Math.cos(d.rotate * cloudRadians),
242
+ wcr = w * cr,
243
+ wsr = w * sr,
244
+ hcr = h * cr,
245
+ hsr = h * sr;
246
+ w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5;
247
+ h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr));
248
+ } else {
249
+ w = (w + 0x1f) >> 5 << 5;
250
+ }
251
+ if (h > maxh) maxh = h;
252
+ if (x + w >= (cw << 5)) {
253
+ x = 0;
254
+ y += maxh;
255
+ maxh = 0;
256
+ }
257
+ if (y + h >= ch) break;
258
+ c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio);
259
+ if (d.rotate) c.rotate(d.rotate * cloudRadians);
260
+ c.fillText(d.text, 0, 0);
261
+ if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0);
262
+ c.restore();
263
+ d.width = w;
264
+ d.height = h;
265
+ d.xoff = x;
266
+ d.yoff = y;
267
+ d.x1 = w >> 1;
268
+ d.y1 = h >> 1;
269
+ d.x0 = -d.x1;
270
+ d.y0 = -d.y1;
271
+ d.hasText = true;
272
+ x += w;
273
+ }
274
+ var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data,
275
+ sprite = [];
276
+ while (--di >= 0) {
277
+ d = data[di];
278
+ if (!d.hasText) continue;
279
+ var w = d.width,
280
+ w32 = w >> 5,
281
+ h = d.y1 - d.y0;
282
+ // Zero the buffer
283
+ for (var i = 0; i < h * w32; i++) sprite[i] = 0;
284
+ x = d.xoff;
285
+ if (x == null) return;
286
+ y = d.yoff;
287
+ var seen = 0,
288
+ seenRow = -1;
289
+ for (var j = 0; j < h; j++) {
290
+ for (var i = 0; i < w; i++) {
291
+ var k = w32 * j + (i >> 5),
292
+ m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0;
293
+ sprite[k] |= m;
294
+ seen |= m;
295
+ }
296
+ if (seen) seenRow = j;
297
+ else {
298
+ d.y0++;
299
+ h--;
300
+ j--;
301
+ y++;
302
+ }
303
+ }
304
+ d.y1 = d.y0 + seenRow;
305
+ d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
306
+ }
307
+ }
308
+
309
+ // Use mask-based collision detection.
310
+ function cloudCollide(tag, board, sw) {
311
+ sw >>= 5;
312
+ var sprite = tag.sprite,
313
+ w = tag.width >> 5,
314
+ lx = tag.x - (w << 4),
315
+ sx = lx & 0x7f,
316
+ msx = 32 - sx,
317
+ h = tag.y1 - tag.y0,
318
+ x = (tag.y + tag.y0) * sw + (lx >> 5),
319
+ last;
320
+ for (var j = 0; j < h; j++) {
321
+ last = 0;
322
+ for (var i = 0; i <= w; i++) {
323
+ if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0))
324
+ & board[x + i]) return true;
325
+ }
326
+ x += sw;
327
+ }
328
+ return false;
329
+ }
330
+
331
+ function cloudBounds(bounds, d) {
332
+ var b0 = bounds[0],
333
+ b1 = bounds[1];
334
+ if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0;
335
+ if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0;
336
+ if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1;
337
+ if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1;
338
+ }
339
+
340
+ function collideRects(a, b) {
341
+ return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y;
342
+ }
343
+
344
+ function archimedeanSpiral(size) {
345
+ var e = size[0] / size[1];
346
+ return function(t) {
347
+ return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)];
348
+ };
349
+ }
350
+
351
+ function rectangularSpiral(size) {
352
+ var dy = 4,
353
+ dx = dy * size[0] / size[1],
354
+ x = 0,
355
+ y = 0;
356
+ return function(t) {
357
+ var sign = t < 0 ? -1 : 1;
358
+ // See triangular numbers: T_n = n * (n + 1) / 2.
359
+ switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) {
360
+ case 0: x += dx; break;
361
+ case 1: y += dy; break;
362
+ case 2: x -= dx; break;
363
+ default: y -= dy; break;
364
+ }
365
+ return [x, y];
366
+ };
367
+ }
368
+
369
+ // TODO reuse arrays?
370
+ function zeroArray(n) {
371
+ var a = [],
372
+ i = -1;
373
+ while (++i < n) a[i] = 0;
374
+ return a;
375
+ }
376
+
377
+ function cloudCanvas() {
378
+ return document.createElement("canvas");
379
+ }
380
+
381
+ function functor(d) {
382
+ return typeof d === "function" ? d : function() { return d; };
383
+ }
384
+
385
+ var spirals = {
386
+ archimedean: archimedeanSpiral,
387
+ rectangular: rectangularSpiral
388
+ };
389
+
390
+ if (typeof module === "object" && module.exports) module.exports = cloud;
391
+ else d3.cloud = cloud;
392
+ });
@@ -0,0 +1 @@
1
+ var cloudRadians=Math.PI/180,cw=1<<11>>5,ch=1<<11;$(function(){var size=[256,256],text=cloudText,font=cloudFont,fontSize=cloudFontSize,fontStyle=cloudFontNormal,fontWeight=cloudFontNormal,rotate=cloudRotate,padding=cloudPadding,spiral=archimedeanSpiral,words=[],timeInterval=Infinity,event=d3.dispatch("word","end"),timer=null,random=Math.random,cloud={},canvas=cloudCanvas;cloud.canvas=function(_){return arguments.length?(canvas=functor(_),cloud):canvas};cloud.start=function(){var contextAndRatio=getContext(canvas()),board=zeroArray((size[0]>>5)*size[1]),bounds=null,n=words.length,i=-1,tags=[],data=words.map(function(d,i){d.text=text.call(this,d,i);d.font=font.call(this,d,i);d.style=fontStyle.call(this,d,i);d.weight=fontWeight.call(this,d,i);d.rotate=rotate.call(this,d,i);d.size= ~~fontSize.call(this,d,i);d.padding=padding.call(this,d,i);return d}).sort(function(a,b){return b.size-a.size});if(timer){clearInterval(timer)}timer=setInterval(step,0);step();return cloud;function step(){var start=Date.now();while(Date.now()-start<timeInterval&& ++i<n&&timer){var d=data[i];d.x=(size[0]*(random()+.5))>>1;d.y=(size[1]*(random()+.5))>>1;cloudSprite(contextAndRatio,d,data,i);if(d.hasText&&place(board,d,bounds)){tags.push(d);event.call("word",cloud,d);if(bounds){cloudBounds(bounds,d)}else{bounds=[{x:d.x+d.x0,y:d.y+d.y0},{x:d.x+d.x1,y:d.y+d.y1}]}d.x-=size[0]>>1;d.y-=size[1]>>1}}if(i>=n){cloud.stop();event.call("end",cloud,tags,bounds)}}};cloud.stop=function(){if(timer){clearInterval(timer);timer=null}return cloud};function getContext(canvas){canvas.width=canvas.height=1;var ratio=Math.sqrt(canvas.getContext("2d").getImageData(0,0,1,1).data.length>>2);canvas.width=(cw<<5)/ratio;canvas.height=ch/ratio;var context=canvas.getContext("2d");context.fillStyle=context.strokeStyle="red";context.textAlign="center";return{context:context,ratio:ratio}}function place(board,tag,bounds){var perimeter=[{x:0,y:0},{x:size[0],y:size[1]}],startX=tag.x,startY=tag.y,maxDelta=Math.sqrt(size[0]*size[0]+size[1]*size[1]),s=spiral(size),dt=random()<.5?1:-1,t= -dt,dxdy,dx,dy;while(dxdy=s(t+=dt)){dx= ~~dxdy[0];dy= ~~dxdy[1];if(Math.min(Math.abs(dx),Math.abs(dy))>=maxDelta){break}tag.x=startX+dx;tag.y=startY+dy;if(tag.x+tag.x0<0||tag.y+tag.y0<0||tag.x+tag.x1>size[0]||tag.y+tag.y1>size[1]){continue}if(!bounds||!cloudCollide(tag,board,size[0])){if(!bounds||collideRects(tag,bounds)){var sprite=tag.sprite,w=tag.width>>5,sw=size[0]>>5,lx=tag.x-(w<<4),sx=lx&0x7f,msx=32-sx,h=tag.y1-tag.y0,x=(tag.y+tag.y0)*sw+(lx>>5),last;for(var j=0;j<h;j+=1){last=0;for(var i=0;i<=w;i+=1){board[x+i]|=(last<<msx)|(i<w?(last=sprite[j*w+i])>>>sx:0)}x+=sw}delete tag.sprite;return true}}}return false}cloud.timeInterval=function(_){return arguments.length?(timeInterval=_==null?Infinity:_,cloud):timeInterval};cloud.words=function(_){return arguments.length?(words=_,cloud):words};cloud.size=function(_){return arguments.length?(size=[+_[0],+_[1]],cloud):size};cloud.font=function(_){return arguments.length?(font=functor(_),cloud):font};cloud.fontStyle=function(_){return arguments.length?(fontStyle=functor(_),cloud):fontStyle};cloud.fontWeight=function(_){return arguments.length?(fontWeight=functor(_),cloud):fontWeight};cloud.rotate=function(_){return arguments.length?(rotate=functor(_),cloud):rotate};cloud.text=function(_){return arguments.length?(text=functor(_),cloud):text};cloud.spiral=function(_){return arguments.length?(spiral=spirals[_]||_,cloud):spiral};cloud.fontSize=function(_){return arguments.length?(fontSize=functor(_),cloud):fontSize};cloud.padding=function(_){return arguments.length?(padding=functor(_),cloud):padding};cloud.random=function(_){return arguments.length?(random=_,cloud):random};cloud.on=function(){var value=event.on.apply(event,arguments);return value===event?cloud:value};function cloudText(d){return d.text}function cloudFont(){return "serif"}function cloudFontNormal(){return "normal"}function cloudFontSize(d){return Math.sqrt(d.value)}function cloudRotate(){return(~~(Math.random()*6)-3)*30}function cloudPadding(){return 1}function cloudSprite(contextAndRatio,d,data,di){if(d.sprite){return}var c=contextAndRatio.context,ratio=contextAndRatio.ratio;c.clearRect(0,0,(cw<<5)/ratio,ch/ratio);var x=0,y=0,maxh=0,n=data.length;di-=1;while(++di<n){d=data[di];c.save();c.font=d.style+" "+d.weight+" "+ ~~((d.size+1)/ratio)+"px "+d.font;var w=c.measureText(d.text+"m").width*ratio,h=d.size<<1;if(d.rotate){var sr=Math.sin(d.rotate*cloudRadians),cr=Math.cos(d.rotate*cloudRadians),wcr=w*cr,wsr=w*sr,hcr=h*cr,hsr=h*sr;w=(Math.max(Math.abs(wcr+hsr),Math.abs(wcr-hsr))+0x1f)>>5<<5;h= ~~Math.max(Math.abs(wsr+hcr),Math.abs(wsr-hcr))}else{w=(w+0x1f)>>5<<5}if(h>maxh){maxh=h}if(x+w>=(cw<<5)){x=0;y+=maxh;maxh=0}if(y+h>=ch){break}c.translate((x+(w>>1))/ratio,(y+(h>>1))/ratio);if(d.rotate){c.rotate(d.rotate*cloudRadians)}c.fillText(d.text,0,0);if(d.padding){c.lineWidth=2*d.padding,c.strokeText(d.text,0,0)}c.restore();d.width=w;d.height=h;d.xoff=x;d.yoff=y;d.x1=w>>1;d.y1=h>>1;d.x0= -d.x1;d.y0= -d.y1;d.hasText=true;x+=w}var pixels=c.getImageData(0,0,(cw<<5)/ratio,ch/ratio).data,sprite=[];while(--di>=0){d=data[di];if(!d.hasText){continue}var w=d.width,w32=w>>5,h=d.y1-d.y0;for(var i=0;i<h*w32;i+=1){sprite[i]=0}x=d.xoff;if(x==null){return}y=d.yoff;var seen=0,seenRow=-1;for(var j=0;j<h;j+=1){for(var i=0;i<w;i+=1){var k=w32*j+(i>>5),m=pixels[((y+j)*(cw<<5)+(x+i))<<2]?1<<(31-(i%32)):0;sprite[k]|=m;seen|=m}if(seen){seenRow=j}else{d.y0+=1;h-=1;j-=1;y+=1}}d.y1=d.y0+seenRow;d.sprite=sprite.slice(0,(d.y1-d.y0)*w32)}}function cloudCollide(tag,board,sw){sw>>=5;var sprite=tag.sprite,w=tag.width>>5,lx=tag.x-(w<<4),sx=lx&0x7f,msx=32-sx,h=tag.y1-tag.y0,x=(tag.y+tag.y0)*sw+(lx>>5),last;for(var j=0;j<h;j+=1){last=0;for(var i=0;i<=w;i+=1){if(((last<<msx)|(i<w?(last=sprite[j*w+i])>>>sx:0))&board[x+i]){return true}}x+=sw}return false}function cloudBounds(bounds,d){var b0=bounds[0],b1=bounds[1];if(d.x+d.x0<b0.x){b0.x=d.x+d.x0}if(d.y+d.y0<b0.y){b0.y=d.y+d.y0}if(d.x+d.x1>b1.x){b1.x=d.x+d.x1}if(d.y+d.y1>b1.y){b1.y=d.y+d.y1}}function collideRects(a,b){return a.x+a.x1>b[0].x&&a.x+a.x0<b[1].x&&a.y+a.y1>b[0].y&&a.y+a.y0<b[1].y}function archimedeanSpiral(size){var e=size[0]/size[1];return function(t){return[e*(t*=.1)*Math.cos(t),t*Math.sin(t)]}}function rectangularSpiral(size){var dy=4,dx=dy*size[0]/size[1],x=0,y=0;return function(t){var sign=t<0?-1:1;switch((Math.sqrt(1+4*sign*t)-sign)&3){case 0:x+=dx;break;case 1:y+=dy;break;case 2:x-=dx;break;default:y-=dy;break}return[x,y]}}function zeroArray(n){var a=[],i=-1;while(++i<n){a[i]=0}return a}function cloudCanvas(){return document.createElement("canvas")}function functor(d){return typeof d==="function"?d:function(){return d}}var spirals={archimedean:archimedeanSpiral,rectangular:rectangularSpiral};if(typeof module==="object"&&module.exports){module.exports=cloud}else{d3.cloud=cloud}});
@@ -0,0 +1 @@
1
+ require 'd3_cloud/rails'
@@ -0,0 +1,2 @@
1
+ require 'd3_cloud/rails/engine'
2
+ require 'd3_cloud/rails/version'
@@ -0,0 +1,6 @@
1
+ module D3Cloud
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module D3Cloud
2
+ module Rails
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: d3-cloud-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Anthony Hernandez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: d3-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.10'
41
+ description: Makes a wordle-like wordcloud script using D3.js available to the asset
42
+ pipeline.
43
+ email:
44
+ - roguegdi27@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - MIT-LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - app/assets/javascripts/d3-cloud.js
53
+ - app/assets/javascripts/d3-cloud.min.js
54
+ - lib/d3-cloud-rails.rb
55
+ - lib/d3_cloud/rails.rb
56
+ - lib/d3_cloud/rails/engine.rb
57
+ - lib/d3_cloud/rails/version.rb
58
+ homepage: https://www.github.com/hernanat/d3-cloud-rails
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.6.13
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A Rails plugin for creating wordclouds using D3.js.
82
+ test_files: []