d3-cloud-rails 0.0.2

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.
@@ -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: []