Skip to content

Instantly share code, notes, and snippets.

@leoh
Forked from ericcoopey/README.md
Last active August 29, 2015 14:11
Show Gist options
  • Select an option

  • Save leoh/2c8e44d465f56f43e320 to your computer and use it in GitHub Desktop.

Select an option

Save leoh/2c8e44d465f56f43e320 to your computer and use it in GitHub Desktop.

Revisions

  1. @ericcoopey ericcoopey revised this gist Aug 29, 2013. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -9,5 +9,5 @@ Example of how to
    3. "Fit" it into the screen. At first the words were appearing out of view to the left.

    Uses Jason Davies excellent word cloud library
    (https://twitter.com/jasondavies)[https://twitter.com/jasondavies]
    (https://github.com/jasondavies/d3-cloud)[https://github.com/jasondavies/d3-cloud]
    [https://twitter.com/jasondavies](https://twitter.com/jasondavies)
    [https://github.com/jasondavies/d3-cloud](https://github.com/jasondavies/d3-cloud)
  2. @ericcoopey ericcoopey revised this gist Aug 29, 2013. 1 changed file with 9 additions and 2 deletions.
    11 changes: 9 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,13 @@
    Word cloud implementation.

    Example of how to
    1. Change style from default

    1. Change style from default. Uses linear scale

    2. Alter word size
    3. "Fit" it into the screen

    3. "Fit" it into the screen. At first the words were appearing out of view to the left.

    Uses Jason Davies excellent word cloud library
    (https://twitter.com/jasondavies)[https://twitter.com/jasondavies]
    (https://github.com/jasondavies/d3-cloud)[https://github.com/jasondavies/d3-cloud]
  3. coop revised this gist Aug 29, 2013. 1 changed file with 0 additions and 0 deletions.
    Binary file modified thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  4. coop revised this gist Aug 29, 2013. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  5. @ericcoopey ericcoopey created this gist Aug 29, 2013.
    6 changes: 6 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    Word cloud implementation.

    Example of how to
    1. Change style from default
    2. Alter word size
    3. "Fit" it into the screen
    401 changes: 401 additions & 0 deletions d3.layout.cloud.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,401 @@
    // Word cloud layout by Jason Davies, http://www.jasondavies.com/word-cloud/
    // Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf
    (function(exports) {
    function cloud() {
    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,
    cloud = {};

    cloud.start = function() {
    var 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 = +new Date,
    d;
    while (+new Date - start < timeInterval && ++i < n && timer) {
    d = data[i];
    d.x = (size[0] * (Math.random() + .5)) >> 1;
    d.y = (size[1] * (Math.random() + .5)) >> 1;
    cloudSprite(d, data, i);
    if (d.hasText && place(board, d, bounds)) {
    tags.push(d);
    event.word(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}];
    // Temporary hack
    d.x -= size[0] >> 1;
    d.y -= size[1] >> 1;
    }
    }
    if (i >= n) {
    cloud.stop();
    event.end(tags, bounds);
    }
    }
    }

    cloud.stop = function() {
    if (timer) {
    clearInterval(timer);
    timer = null;
    }
    return cloud;
    };

    cloud.timeInterval = function(x) {
    if (!arguments.length) return timeInterval;
    timeInterval = x == null ? Infinity : x;
    return cloud;
    };

    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 = Math.random() < .5 ? 1 : -1,
    t = -dt,
    dxdy,
    dx,
    dy;

    while (dxdy = s(t += dt)) {
    dx = ~~dxdy[0];
    dy = ~~dxdy[1];

    if (Math.min(dx, 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;
    // TODO only check for collisions within current bounds.
    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++) {
    last = 0;
    for (var i = 0; i <= w; i++) {
    board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0);
    }
    x += sw;
    }
    delete tag.sprite;
    return true;
    }
    }
    }
    return false;
    }

    cloud.words = function(x) {
    if (!arguments.length) return words;
    words = x;
    return cloud;
    };

    cloud.size = function(x) {
    if (!arguments.length) return size;
    size = [+x[0], +x[1]];
    return cloud;
    };

    cloud.font = function(x) {
    if (!arguments.length) return font;
    font = d3.functor(x);
    return cloud;
    };

    cloud.fontStyle = function(x) {
    if (!arguments.length) return fontStyle;
    fontStyle = d3.functor(x);
    return cloud;
    };

    cloud.fontWeight = function(x) {
    if (!arguments.length) return fontWeight;
    fontWeight = d3.functor(x);
    return cloud;
    };

    cloud.rotate = function(x) {
    if (!arguments.length) return rotate;
    rotate = d3.functor(x);
    return cloud;
    };

    cloud.text = function(x) {
    if (!arguments.length) return text;
    text = d3.functor(x);
    return cloud;
    };

    cloud.spiral = function(x) {
    if (!arguments.length) return spiral;
    spiral = spirals[x + ""] || x;
    return cloud;
    };

    cloud.fontSize = function(x) {
    if (!arguments.length) return fontSize;
    fontSize = d3.functor(x);
    return cloud;
    };

    cloud.padding = function(x) {
    if (!arguments.length) return padding;
    padding = d3.functor(x);
    return cloud;
    };

    return d3.rebind(cloud, event, "on");
    }

    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;
    }

    // Fetches a monochrome sprite bitmap for the specified text.
    // Load in batches for speed.
    function cloudSprite(d, data, di) {
    if (d.sprite) return;
    c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio);
    var x = 0,
    y = 0,
    maxh = 0,
    n = data.length;
    --di;
    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;
    // Zero the buffer
    for (var i = 0; i < h * w32; i++) 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++) {
    for (var i = 0; i < w; i++) {
    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++;
    h--;
    j--;
    y++;
    }
    }
    d.y1 = d.y0 + seenRow;
    d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32);
    }
    }

    // Use mask-based collision detection.
    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++) {
    last = 0;
    for (var i = 0; i <= w; i++) {
    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;
    // See triangular numbers: T_n = n * (n + 1) / 2.
    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];
    };
    }

    // TODO reuse arrays?
    function zeroArray(n) {
    var a = [],
    i = -1;
    while (++i < n) a[i] = 0;
    return a;
    }

    var cloudRadians = Math.PI / 180,
    cw = 1 << 11 >> 5,
    ch = 1 << 11,
    canvas,
    ratio = 1;

    if (typeof document !== "undefined") {
    canvas = document.createElement("canvas");
    canvas.width = 1;
    canvas.height = 1;
    ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
    canvas.width = (cw << 5) / ratio;
    canvas.height = ch / ratio;
    } else {
    // node-canvas support
    var Canvas = require("canvas");
    canvas = new Canvas(cw << 5, ch);
    }

    var c = canvas.getContext("2d"),
    spirals = {
    archimedean: archimedeanSpiral,
    rectangular: rectangularSpiral
    };
    c.fillStyle = c.strokeStyle = "red";
    c.textAlign = "center";

    exports.cloud = cloud;
    })(typeof exports === "undefined" ? d3.layout || (d3.layout = {}) : exports);
    71 changes: 71 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,71 @@
    <!DOCTYPE html>
    <html>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="d3.layout.cloud.js"></script>
    <head>
    <title>Word Cloud Example</title>
    </head>
    <style>
    body {
    font-family:"Lucida Grande","Droid Sans",Arial,Helvetica,sans-serif;
    }
    .legend {
    border: 1px solid #555555;
    border-radius: 5px 5px 5px 5px;
    font-size: 0.8em;
    margin: 10px;
    padding: 8px;
    }
    .bld {
    font-weight: bold;
    }
    </style>
    <body>

    </body>
    <script>

    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}];


    var color = d3.scale.linear()
    .domain([0,1,2,3,4,5,6,10,15,20,100])
    .range(["#ddd", "#ccc", "#bbb", "#aaa", "#999", "#888", "#777", "#666", "#555", "#444", "#333", "#222"]);

    d3.layout.cloud().size([800, 300])
    .words(frequency_list)
    .rotate(0)
    .fontSize(function(d) { return d.size; })
    .on("end", draw)
    .start();

    function draw(words) {
    d3.select("body").append("svg")
    .attr("width", 850)
    .attr("height", 350)
    .attr("class", "wordcloud")
    .append("g")
    // without the transform, words words would get cutoff to the left and top, they would
    // appear outside of the SVG area
    .attr("transform", "translate(320,200)")
    .selectAll("text")
    .data(words)
    .enter().append("text")
    .style("font-size", function(d) { return d.size + "px"; })
    .style("fill", function(d, i) { return color(i); })
    .attr("transform", function(d) {
    return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
    })
    .text(function(d) { return d.text; });
    }
    </script>

    <div style="width: 40%;">
    <div class="legend">
    Commonly used words are larger and slightly faded in color. Less common words are smaller and darker.
    </div>

    </div>


    </html>