Skip to content

Instantly share code, notes, and snippets.

@hl2055
Forked from christophermanning/README.md
Last active September 17, 2015 20:05
Show Gist options
  • Save hl2055/96ab36027d99d56084b6 to your computer and use it in GitHub Desktop.
Save hl2055/96ab36027d99d56084b6 to your computer and use it in GitHub Desktop.
Random Walk USA
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
width = Math.max(window.innerWidth, 960)
height = Math.max(window.innerHeight, 500)
config = {"theta": 1, "density" : 10, "scale": 25, "careful": true, "smooth": true};
gui = new dat.GUI({width: 130});
var examples = gui.addFolder('Examples');
examples.open()
config.Sketch = function(){
config["theta"] = 1
config["density"] = 10
config["scale"] = 25
config["careful"] = true
config["smooth"] = true
draw()
}
examples.add(config, "Sketch")
config.Geom = function(){
config["theta"] = 90
config["density"] = 10
config["scale"] = 25
config["careful"] = true
config["smooth"] = true
draw()
}
examples.add(config, "Geom")
config.Squares = function(){
config["theta"] = 90
config["density"] = 30
config["scale"] = 5
config["careful"] = true
config["smooth"] = false
draw()
}
examples.add(config, "Squares")
config.Triangles = function(){
config["theta"] = 60
config["density"] = 1
config["scale"] = 25
config["careful"] = false
config["smooth"] = false
draw()
}
examples.add(config, "Triangles")
config.Random = function(){
gui.__folders.Settings.__controllers.forEach(function(c){
if(typeof(c.__select) != 'undefined') {
c.setValue(c.__select[Math.floor(Math.random()*(c.__select.length-1))].value)
} else {
if(typeof c.initialValue == "boolean") {
c.setValue(Math.round(Math.random()) == true)
} else if(typeof c.initialValue == "number") {
c.setValue(Math.floor(Math.random() * c.__max) + c.__min)
}
}
})
draw()
}
examples.add(config, "Random")
var settings = gui.addFolder('Settings');
thetaChanger = settings.add(config, "theta", 1, 180).step(1).listen()
thetaChanger.onChange(function(value) {
draw()
});
scaleChanger = settings.add(config, "scale", 1, 25).step(1).listen()
scaleChanger.onChange(function(value) {
draw()
});
densityChanger = settings.add(config, "density", 1, 50).listen()
densityChanger.onChange(function(value) {
draw()
});
carefulChanger = settings.add(config, "careful").listen()
carefulChanger.onChange(function(value) {
draw()
});
smoothChanger = settings.add(config, "smooth").listen()
smoothChanger.onChange(function(value) {
draw()
});
config.redraw = function(){
draw()
}
settings.add(config, "redraw")
var zoom = d3.behavior.zoom()
.scale(config["theta"])
.scaleExtent([1, 180])
.on("zoom", function(d,i) {
config["theta"] = Math.floor(d3.event.scale)
draw()
});
var projection = d3.geo.albersUsa()
.scale(1000)
.translate([(width-100) / 2, (height-25) / 2]);
var path = d3.geo.path()
.projection(projection);
canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height)
.call(zoom)
context = canvas.node().getContext("2d")
color = d3.scale.category20()
line = d3.svg.line()
.interpolate(config["interpolation"])
.tension(config["tension"])
.x(function(d, i) { return d.xo })
.y(function(d, i) { return d.yo })
function pointInPolygon(point, polygon) {
for (var n = polygon.length, i = 0, j = n - 1, x = point[0], y = point[1], inside = false; i < n; j = i++) {
var xi = polygon[i][0], yi = polygon[i][1],
xj = polygon[j][0], yj = polygon[j][1];
if ((yi > y ^ yj > y) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) inside = !inside;
}
return inside;
}
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
d3.json('us.json', function(error, us) {
us_json = us
us_json.objects.states.geometries = us_json.objects.states.geometries.filter(function(d) { return d.id < 60 })
states = topojson.feature(us_json, us_json.objects.states).features,
neighbors = topojson.neighbors(us_json.objects.states.geometries);
draw()
})
function draw(){
context.clearRect(0, 0, width, height)
context.lineJoin = 'round'
for (var i0 = 0; i0 < states.length; i0++) {
d = states[i0]
context.beginPath()
context.strokeStyle = states[i0].color ? states[i0].color : states[i0].color = color(states[i0].colorIndex = d3.max(neighbors[i0], function(n) { return states[n].colorIndex; }) + 1 | 0)
for(var i1=0; i1<d.geometry.coordinates.length; i1++) {
if(d.geometry.type=="MultiPolygon") {
p = d.geometry.coordinates[i1][0]
}else{
p = d.geometry.coordinates[i1]
}
d2 = {type: "Feature", properties: {}, geometry: {type: "Polygon", coordinates: [p]}}
centroid = path.centroid(d2)
area = path.area(d2)
// skip small lands unless they're AK or HI
if(area<100 && d.id != 2 && d.id != 15) continue
last = {xo: centroid[0], yo: centroid[1], pip: true}
context.moveTo(last.xo, last.yo)
strokes = area*(config["density"]*.01)
for(var i2=0;i2<(strokes < 4 ? 4 : strokes);i2++){
xo = last.xo
yo = last.yo
if(last.pip) {
// random angle that's a multiple of theta
r = randInt(1, 360/config["theta"])
angle = r * config["theta"]
theta = angle * (Math.PI/180)
} else {
// if we're outside of the polygon, turn towards the centroid
dx = centroid[0] - xo
dy = centroid[1] - yo
theta = Math.atan2(dy, dx)
thetaDeg = theta * (180/Math.PI)
// random angle towards the centroid clamped to theta
randAngle = randInt(thetaDeg - 90, thetaDeg + 90)
angle = Math.floor(randAngle/config["theta"])*config["theta"]
theta = Math.PI * (angle/180)
}
scale = Math.sqrt(area) < config["scale"] ? Math.sqrt(area) : config["scale"]
xo += Math.cos(theta)*scale
yo += Math.sin(theta)*scale
pip = pointInPolygon(projection.invert([xo, yo]), d2.geometry.coordinates[0])
last = {xo: xo, yo: yo, pip: pip}
if(pip || i2==0 || !(pip || config["careful"])) context.lineTo(last.xo, last.yo)
if(!config["smooth"]) context.moveTo(last.xo, last.yo)
}
}
context.stroke()
}
}
</script>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment