<meta charset="utf-8">
<title>Word Cloud Generator</title>
<link href="cloud.css" rel="stylesheet">
<link rel="image_src" href="amazing.png">
<div id="vis"></div>
<form id="form">
<p style="position: absolute; right: 0; top: 0" id="status"></p>
<div style="text-align: center">
<div id="presets"></div>
<div id="custom-area">
<p><label for="text">Enter a URL below, or paste some text.</label>
<p><textarea id="text"></textarea>
<button id="go" type="submit">Go!</button>
</div>
</div>
<hr>
<div style="float: right; text-align: right">
<p><label for="max">Number of words:</label> <input type="number" value="250" min="1" id="max">
<p><label for="per-line"><input type="checkbox" id="per-line"> One word per line</label>
<!--<p><label for="colours">Colours:</label> <a href="#" id="random-palette">get random palette</a>-->
<p><label>Download:</label>
<a id="download-svg" href="#" target="_blank">SVG</a> |
<a id="download-png" href="#" target="_blank">PNG</a>
</div>
<div style="float: left">
<p><label>Spiral:</label>
<label for="archimedean"><input type="radio" name="spiral" id="archimedean" value="archimedean" checked="checked"> Archimedean</label>
<label for="rectangular"><input type="radio" name="spiral" id="rectangular" value="rectangular"> Rectangular</label>
<p><label for="scale">Scale:</label>
<label for="scale-log"><input type="radio" name="scale" id="scale-log" value="log" checked="checked"> log n</label>
<label for="scale-sqrt"><input type="radio" name="scale" id="scale-sqrt" value="sqrt"> √n</label>
<label for="scale-linear"><input type="radio" name="scale" id="scale-linear" value="linear"> n</label>
<p><label for="font">Font:</label> <input type="text" id="font" value="Impact">
</div>
<div id="angles">
<p><input type="number" id="angle-count" value="5" min="1"> <label for="angle-count">orientations</label>
<label for="angle-from">from</label> <input type="number" id="angle-from" value="-60" min="-90" max="90"> °
<label for="angle-to">to</label> <input type="number" id="angle-to" value="60" min="-90" max="90"> °
</div>
<hr style="clear: both">
<p style="float: right"><a href="about/">How the Word Cloud Generator Works</a>.
<p>Copyright © <a href="http://www.jasondavies.com/">Jason Davies</a> 2013.
</form>
<script src="d3.min.js"></script>
<script src="d3.layout.cloud.js"></script>
<script src="cloud.js"></script>
===================cloud css=======
body {
position: relative;
font-family: "Helvetica Neue", sans-serif;
width: 960px;
margin: auto;
margin-top: 20px;
margin-bottom: 1em;
}
#vis text:hover { opacity: .7 !important; }
#presets a { border-left: solid #666 1px; padding: 0 10px; }
#presets a.first { border-left: none; }
#keyword { width: 300px; }
#fetcher { width: 500px; }
#keyword, #go { font-size: 1.5em; }
#text { width: 100%; height: 100px; }
p.copy { font-size: small; }
#form { font-size: small; position: relative; }
hr { border: none; border-bottom: solid #ccc 1px; }
a.active { text-decoration: none; color: #000; font-weight: bold; cursor: text; }
#angles line, #angles path, #angles circle { stroke: #666; }
#angles text { fill: #333; }
#angles path.drag { fill: #666; cursor: move; }
#angles { text-align: center; margin: 0 auto; width: 350px; }
#angles input, #max { width: 42px; }
/* Code styles from Mike Bostock's excellent tutorials,
* http://bost.ocks.org/mike */
pre, code { font-family: "Menlo", monospace; }
.html .value,
.javascript .string,
.javascript .regexp {
color: #756bb1;
}
.html .tag,
.css .tag,
.javascript .keyword {
color: #3182bd;
}
.comment {
color: #636363;
}
.html .doctype,
.javascript .number {
color: #31a354;
}
.html .attribute,
.css .attribute,
.javascript .class,
.javascript .special {
color: #e6550d;
}
=========cloud js======
var fill = d3.scale.category20b();
var w = 960,
h = 600;
var words = [],
max,
scale = 1,
complete = 0,
keyword = "",
tags,
fontSize,
maxLength = 30,
fetcher = "The sun did not shine. It was too wet to play. So we sat in the house All that cold, cold, wet day.I sat there with Sally, we sat there we two. And I said",
statusText = d3.select("#status");
var layout = d3.layout.cloud()
.timeInterval(10)
.size([w, h])
.fontSize(function(d) { return fontSize(+d.value); })
.text(function(d) { return d.key; })
.on("word", progress)
.on("end", draw);
var svg = d3.select("#vis").append("svg")
.attr("width", w)
.attr("height", h);
var background = svg.append("g"),
vis = svg.append("g")
.attr("transform", "translate(" + [w >> 1, h >> 1] + ")");
d3.select("#download-svg").on("click", downloadSVG);
d3.select("#download-png").on("click", downloadPNG);
d3.select(window).on("hashchange", hashchange);
d3.select(window).on("load", hashchange);
var form = d3.select("#form")
.on("submit", function() {
load(d3.select("#text").property("value"));
d3.event.preventDefault();
});
form.selectAll("input[type=number]")
.on("click.refresh", function() {
if (this.value === this.defaultValue) return;
generate();
this.defaultValue = this.value;
});
form.selectAll("input[type=radio], #font")
.on("change", generate);
// From Jonathan Feinberg's cue.language, see lib/cue.language/license.txt.
var stopWords = /^(i|me|my|myself|we|us|our|ours|ourselves|you|your|yours|yourself|yourselves|he|him|his|himself|she|her|hers|herself|it|its|itself|they|them|their|theirs|themselves|what|which|who|whom|whose|this|that|these|those|am|is|are|was|were|be|been|being|have|has|had|having|do|does|did|doing|will|would|should|can|could|ought|i'm|you're|he's|she's|it's|we're|they're|i've|you've|we've|they've|i'd|you'd|he'd|she'd|we'd|they'd|i'll|you'll|he'll|she'll|we'll|they'll|isn't|aren't|wasn't|weren't|hasn't|haven't|hadn't|doesn't|don't|didn't|won't|wouldn't|shan't|shouldn't|can't|cannot|couldn't|mustn't|let's|that's|who's|what's|here's|there's|when's|where's|why's|how's|a|an|the|and|but|if|or|because|as|until|while|of|at|by|for|with|about|against|between|into|through|during|before|after|above|below|to|from|up|upon|down|in|out|on|off|over|under|again|further|then|once|here|there|when|where|why|how|all|any|both|each|few|more|most|other|some|such|no|nor|not|only|own|same|so|than|too|very|say|says|said|shall)$/,
punctuation = /[!"&()*+,-\.\/:;<=>?\[\\\]^`\{|\}~]+/g,
wordSeparators = /[\s\u3031-\u3035\u309b\u309c\u30a0\u30fc\uff70]+/g,
discard = /^(@|https?:)/,
htmlTags = /(<[^>]*?>|<script.*?<\/script>|<style.*?<\/style>|<head.*?><\/head>)/g,
matchTwitter = /^https?:\/\/([^\.]*\.)?twitter\.com/;
function parseHTML(d) {
parseText(d.replace(htmlTags, " ").replace(/&#(x?)([\dA-Fa-f]{1,4});/g, function(d, hex, m) {
return String.fromCharCode(+((hex ? "0x" : "") + m));
}).replace(/&\w+;/g, " "));
}
function getURL(url, callback) {
statusText.text("Fetching… ");
if (matchTwitter.test(url)) {
var iframe = d3.select("body").append("iframe").style("display", "none");
d3.select(window).on("message", function() {
var json = JSON.parse(d3.event.data);
callback((Array.isArray(json) ? json : json.results).map(function(d) { return d.text; }).join("\n\n"));
iframe.remove();
});
iframe.attr("src", "http://jsonp.jasondavies.com/?" + encodeURIComponent(url));
return;
}
try {
d3.text(url, function(text) {
if (text == null) proxy(url, callback);
else callback(text);
});
} catch(e) {
proxy(url, callback);
}
}
function proxy(url, callback) {
d3.text("//www.jasondavies.com/xhr?url=" + encodeURIComponent(url), callback);
}
function flatten(o, k) {
if (typeof o === "string") return o;
var text = [];
for (k in o) {
var v = flatten(o[k], k);
if (v) text.push(v);
}
return text.join(" ");
}
function parseText(text) {
tags = {};
var cases = {};
text.split(d3.select("#per-line").property("checked") ? /\n/g : wordSeparators).forEach(function(word) {
if (discard.test(word)) return;
word = word.replace(punctuation, "");
if (stopWords.test(word.toLowerCase())) return;
word = word.substr(0, maxLength);
cases[word.toLowerCase()] = word;
tags[word = word.toLowerCase()] = (tags[word] || 0) + 1;
});
tags = d3.entries(tags).sort(function(a, b) { return b.value - a.value; });
tags.forEach(function(d) { d.key = cases[d.key]; });
generate();
}
function generate() {
layout
.font(d3.select("#font").property("value"))
.spiral(d3.select("input[name=spiral]:checked").property("value"));
fontSize = d3.scale[d3.select("input[name=scale]:checked").property("value")]().range([10, 100]);
if (tags.length) fontSize.domain([+tags[tags.length - 1].value || 1, +tags[0].value]);
complete = 0;
statusText.style("display", null);
words = [];
layout.stop().words(tags.slice(0, max = Math.min(tags.length, +d3.select("#max").property("value")))).start();
}
function progress(d) {
statusText.text(++complete + "/" + max);
}
function draw(data, bounds) {
statusText.style("display", "none");
scale = bounds ? Math.min(
w / Math.abs(bounds[1].x - w / 2),
w / Math.abs(bounds[0].x - w / 2),
h / Math.abs(bounds[1].y - h / 2),
h / Math.abs(bounds[0].y - h / 2)) / 2 : 1;
words = data;
var text = vis.selectAll("text")
.data(words, function(d) { return d.text.toLowerCase(); });
text.transition()
.duration(1000)
.attr("transform", function(d) { return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; })
.style("font-size", function(d) { return d.size + "px"; });
text.enter().append("text")
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; })
.style("font-size", function(d) { return d.size + "px"; })
.on("click", function(d) {
load(d.text);
})
.style("opacity", 1e-6)
.transition()
.duration(1000)
.style("opacity", 1);
text.style("font-family", function(d) { return d.font; })
.style("fill", function(d) { return fill(d.text.toLowerCase()); })
.text(function(d) { return d.text; });
var exitGroup = background.append("g")
.attr("transform", vis.attr("transform"));
var exitGroupNode = exitGroup.node();
text.exit().each(function() {
exitGroupNode.appendChild(this);
});
exitGroup.transition()
.duration(1000)
.style("opacity", 1e-6)
.remove();
vis.transition()
.delay(1000)
.duration(750)
.attr("transform", "translate(" + [w >> 1, h >> 1] + ")scale(" + scale + ")");
}
// Converts a given word cloud to image/png.
function downloadPNG() {
var canvas = document.createElement("canvas"),
c = canvas.getContext("2d");
canvas.width = w;
canvas.height = h;
c.translate(w >> 1, h >> 1);
c.scale(scale, scale);
words.forEach(function(word, i) {
c.save();
c.translate(word.x, word.y);
c.rotate(word.rotate * Math.PI / 180);
c.textAlign = "center";
c.fillStyle = fill(word.text.toLowerCase());
c.font = word.size + "px " + word.font;
c.fillText(word.text, 0, 0);
c.restore();
});
d3.select(this).attr("href", canvas.toDataURL("image/png"));
}
function downloadSVG() {
d3.select(this).attr("href", "data:image/svg+xml;charset=utf-8;base64," + btoa(unescape(encodeURIComponent(
svg.attr("version", "1.1")
.attr("xmlns", "http://www.w3.org/2000/svg")
.node().parentNode.innerHTML))));
}
function hashchange() {
var h = location.hash;
if (h && h.length > 1) {
h = decodeURIComponent(h.substr(1));
if (h !== fetcher) load(h);
} else load(fetcher);
}
function load(f) {
f = f || fetcher;
fetcher = f;
var h = /^https?:\/\//.test(fetcher)
? "#" + encodeURIComponent(fetcher)
: "";
if (fetcher != null) d3.select("#text").property("value", fetcher);
if (location.hash !== h) location.hash = h;
if (h) getURL(fetcher, parseHTML);
else if (fetcher) parseText(fetcher);
}
d3.select("#random-palette").on("click", function() {
paletteJSON("http://www.colourlovers.com/api/palettes/random", {}, function(d) {
fill.range(d[0].colors);
vis.selectAll("text")
.style("fill", function(d) { return fill(d.text.toLowerCase()); });
});
d3.event.preventDefault();
});
(function() {
var r = 40.5,
px = 35,
py = 20;
var angles = d3.select("#angles").append("svg")
.attr("width", 2 * (r + px))
.attr("height", r + 1.5 * py)
.append("g")
.attr("transform", "translate(" + [r + px, r + py] +")");
angles.append("path")
.style("fill", "none")
.attr("d", ["M", -r, 0, "A", r, r, 0, 0, 1, r, 0].join(" "));
angles.append("line")
.attr("x1", -r - 7)
.attr("x2", r + 7);
angles.append("line")
.attr("y2", -r - 7);
angles.selectAll("text")
.data([-90, 0, 90])
.enter().append("text")
.attr("dy", function(d, i) { return i === 1 ? null : ".3em"; })
.attr("text-anchor", function(d, i) { return ["end", "middle", "start"][i]; })
.attr("transform", function(d) {
d += 90;
return "rotate(" + d + ")translate(" + -(r + 10) + ")rotate(" + -d + ")translate(2)";
})
.text(function(d) { return d + "°"; });
var radians = Math.PI / 180,
from,
to,
count,
scale = d3.scale.linear(),
arc = d3.svg.arc()
.innerRadius(0)
.outerRadius(r);
d3.selectAll("#angle-count, #angle-from, #angle-to")
.on("change", getAngles)
.on("mouseup", getAngles);
getAngles();
function getAngles() {
count = +d3.select("#angle-count").property("value");
from = Math.max(-90, Math.min(90, +d3.select("#angle-from").property("value")));
to = Math.max(-90, Math.min(90, +d3.select("#angle-to").property("value")));
update();
}
function update() {
scale.domain([0, count - 1]).range([from, to]);
var step = (to - from) / count;
var path = angles.selectAll("path.angle")
.data([{startAngle: from * radians, endAngle: to * radians}]);
path.enter().insert("path", "circle")
.attr("class", "angle")
.style("fill", "#fc0");
path.attr("d", arc);
var line = angles.selectAll("line.angle")
.data(d3.range(count).map(scale));
line.enter().append("line")
.attr("class", "angle");
line.exit().remove();
line.attr("transform", function(d) { return "rotate(" + (90 + d) + ")"; })
.attr("x2", function(d, i) { return !i || i === count - 1 ? -r - 5 : -r; });
var drag = angles.selectAll("path.drag")
.data([from, to]);
drag.enter().append("path")
.attr("class", "drag")
.attr("d", "M-9.5,0L-3,3.5L-3,-3.5Z")
.call(d3.behavior.drag()
.on("drag", function(d, i) {
d = (i ? to : from) + 90;
var start = [-r * Math.cos(d * radians), -r * Math.sin(d * radians)],
m = [d3.event.x, d3.event.y],
delta = ~~(Math.atan2(cross(start, m), dot(start, m)) / radians);
d = Math.max(-90, Math.min(90, d + delta - 90)); // remove this for 360°
delta = to - from;
if (i) {
to = d;
if (delta > 360) from += delta - 360;
else if (delta < 0) from = to;
} else {
from = d;
if (delta > 360) to += 360 - delta;
else if (delta < 0) to = from;
}
update();
})
.on("dragend", generate));
drag.attr("transform", function(d) { return "rotate(" + (d + 90) + ")translate(-" + r + ")"; });
layout.rotate(function() {
return scale(~~(Math.random() * count));
});
d3.select("#angle-count").property("value", count);
d3.select("#angle-from").property("value", from);
d3.select("#angle-to").property("value", to);
}
function cross(a, b) { return a[0] * b[1] - a[1] * b[0]; }
function dot(a, b) { return a[0] * b[0] + a[1] * b[1]; }
})();
========== d3.layout js =======
// 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,
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) {
return {
text: text.call(this, d, i),
font: font.call(this, d, i),
rotate: rotate.call(this, d, i),
size: ~~fontSize.call(this, d, i),
padding: cloudPadding.call(this, d, i)
};
}).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 (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.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 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.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);
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;
x += w;
}
var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data,
sprite = [];
while (--di >= 0) {
d = data[di];
var w = d.width,
w32 = w >> 5,
h = d.y1 - d.y0,
p = d.padding;
// 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;
if (p) {
if (j) sprite[k - w32] |= m;
if (j < w - 1) sprite[k + w32] |= m;
m |= (m << 1) | (m >> 1);
}
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 = "red";
c.textAlign = "center";
exports.cloud = cloud;
})(typeof exports === "undefined" ? d3.layout || (d3.layout = {}) : exports);