Strange game. The only way to win is not to play.
Last active
October 27, 2019 19:51
-
-
Save emeeks/912e24404b85a2af7dc7cb17927e816d to your computer and use it in GitHub Desktop.
Wargames Aesthetic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function circlePath(radius) { | |
let arc = d3.arc().outerRadius(radius); | |
return arc({ startAngle: 0, endAngle: 8 }) | |
} | |
function rectPath(width,height) { | |
return "M0,0L" + width + ",0L" + width + "," + height + "L0," + height + "Z" | |
} | |
function linePath(x1,x2,y1,y2) { | |
return "M" + x1 + "," + y1 + "L" + x2 + "," + y2 + "L" | |
} | |
function jitterLine (pathNode) { | |
let length = pathNode.getTotalLength(); | |
let j = 2; | |
let x = j + Math.random() * j * 5; | |
let jitteredPoints = []; | |
let lineGen = d3.line() | |
.x(d => d.x) | |
.y(d => d.y) | |
.curve(d3.curveCatmullRom.alpha(0.5)); | |
let newPoint = pathNode.getPointAtLength(0); | |
jitteredPoints.push(newPoint); | |
while (x < length) { | |
newPoint = pathNode.getPointAtLength(x); | |
let newX = newPoint.x + (Math.random() * j - j/2); | |
let newY = newPoint.y + (Math.random() * j - j/2) | |
jitteredPoints.push({ x: newX, y: newY }) | |
x += j + Math.random() * j * 5; | |
} | |
newPoint = pathNode.getPointAtLength(length); | |
jitteredPoints.push(newPoint); | |
return lineGen(jitteredPoints); | |
} | |
function cheapSketchy(path) { | |
let length = path.getTotalLength(); | |
let drawCode = ""; | |
let x = 0; | |
let step = 8; | |
while (x < length / 2) { | |
let start = path.getPointAtLength(x); | |
let end = path.getPointAtLength(length - x); | |
drawCode += " M" + | |
(start.x + (Math.random() * step - step/2)) + " " + | |
(start.y + (Math.random() * step - step/2)) + "L" + | |
(end.x + (Math.random() * step - step/2)) + " " + | |
(end.y + (Math.random() * step - step/2)); | |
x += step + Math.random() * step; | |
} | |
return drawCode; | |
} | |
function cheapPopArtsy (path) { | |
let length = path.getTotalLength(); | |
let circles = [] | |
let x = 0; | |
let step = 8; | |
while (x < length / 2) { | |
let start = path.getPointAtLength(x); | |
let end = path.getPointAtLength(length - x); | |
let begin = 0.75 | |
while (begin < step) { | |
const percent = begin / step | |
const circleXa = percent * start.x | |
const circleXb = (1 - percent) * end.x | |
const circleYa = percent * start.y | |
const circleYb = (1 - percent) * end.y | |
circles.push([circleXa + circleXb, circleYa + circleYb]) | |
begin = begin + (1 + Math.random()) | |
} | |
x = x + step | |
} | |
return circles; | |
} | |
randomColor = function (baseColor,range) { | |
var hslBase = d3.hsl(baseColor) | |
hslBase.h = hslBase.h + (Math.floor(Math.random() * (range * 255)) - Math.floor(range / 2)); | |
hslBase.s = hslBase.s + (Math.floor(Math.random() * range) - Math.floor(range / 2)); | |
hslBase.l = hslBase.l + (Math.floor(Math.random() * range) - Math.floor(range / 2)); | |
return hslBase.toString(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global d3:false */ | |
/*jshint unused:false*/ | |
/** | |
* Initiate the sketchy library | |
* @constructor | |
*/ | |
var d3sketchy = function(){ | |
/** | |
* Default attributes for generating the shapes, doing this we don't need to check if all parameters are provided | |
* And if someone wants to build a lot of shapes with the same properties she just needs to use "setDefaults" to change them | |
* @type {object} | |
* @defaultvalue | |
*/ | |
var defaults = { | |
x:0, | |
y:0, | |
width:20, | |
height:20, | |
sketch:1, | |
density:1, | |
radius:10, | |
angle:45, | |
count:2, | |
shape:"circle", | |
clip:"", | |
margin:2 | |
}; | |
/** | |
* Changing the default attributes | |
* @param {object} opts - object with default attributes see "var defaults" | |
* @return {object} defaults - the full default object | |
*/ | |
function setDefaults(opts){ | |
defaults = extend(defaults, opts); | |
return defaults; | |
} | |
/** | |
* merging two objects, source will replace duplicates in destination | |
* @param {object} destination | |
* @param {object} source | |
*/ | |
function extend(destination, source) { | |
var returnObj = {}, attrname; | |
for (attrname in destination) { returnObj[attrname] = destination[attrname]; } | |
for (attrname in source) { returnObj[attrname] = source[attrname]; } | |
return returnObj; | |
} | |
/** | |
* Generate random number between min and max | |
* @param {float|int} min | |
* @param {float|int} max | |
* @return {float} | |
*/ | |
function rand(min, max){ | |
return Math.random()* (max-min) + min; | |
} | |
/** | |
* Create sketchy | |
* @constructor | |
*/ | |
function sketchy(){ | |
} | |
/** | |
* drawing a sketchy line | |
* this is kind of the heart of the whole tool. | |
* so if you want to make changes to the appearance of the lines, tweak the following lines | |
* @param {object} opts | |
* @param {d3.selection} opts.svg | |
* @param {float|int} opts.x1 - x point 1 | |
* @param {float|int} opts.y1 - y point 1 | |
* @param {float|int} opts.x2 - x point 2 | |
* @param {float|int} opts.y2 - y point 2 | |
* @param {object} opts.sketch | |
* @param {object} opts.sketch.x - sketchiness on the x-axis | |
* @param {object} opts.sketch.y - sketchiness on the y-axis | |
*/ | |
sketchy.drawLine = function(opts){ | |
//Each line is drawn twice the increase sketchiness | |
for(var i = 1; i<3; i++){ | |
var or2 = rand(0.2, 0.8); | |
var cx2 = opts.x1+ (opts.x2-opts.x1)*or2+rand(-1,1); | |
var cy2 = opts.y1+ (opts.y2-opts.y1)*or2+rand(-1,1); | |
var or1 = or2 + rand(-0.3, -0.2); | |
var cx1 = opts.x1+ (opts.x2-opts.x1)*or1+rand(-1,1); | |
var cy1 = opts.y1+ (opts.y2-opts.y1)*or1+rand(-1,1); | |
opts.svg.append("path") | |
.attr("d", "M"+ | |
(opts.x1 + rand(-1,0)*opts.sketch.x/i)+" "+ | |
(opts.y1 + rand(-1,1)*opts.sketch.y/i)+" Q"+ | |
cx1+" "+cy1+" "+ | |
cx2+" "+cy2+" T"+ | |
(opts.x2 + rand(0,1)*opts.sketch.x/i)+" "+ | |
(opts.y2 + rand(-1,1)*opts.sketch.y/i)); | |
} | |
}; | |
sketchy.drawLineSinglePath = function(opts){ | |
//Each line is drawn twice the increase sketchiness | |
var sketching = ""; | |
for(var i = 1; i<3; i++){ | |
var or2 = rand(0.2, 0.8); | |
var cx2 = opts.x1+ (opts.x2-opts.x1)*or2+rand(-1,1); | |
var cy2 = opts.y1+ (opts.y2-opts.y1)*or2+rand(-1,1); | |
var or1 = or2 + rand(-0.3, -0.2); | |
var cx1 = opts.x1+ (opts.x2-opts.x1)*or1+rand(-1,1); | |
var cy1 = opts.y1+ (opts.y2-opts.y1)*or1+rand(-1,1); | |
sketching += " M"+ | |
(opts.x1 + rand(-1,0)*opts.sketch.x/i)+" "+ | |
(opts.y1 + rand(-1,1)*opts.sketch.y/i)+" Q"+ | |
cx1+" "+cy1+" "+ | |
cx2+" "+cy2+" T"+ | |
(opts.x2 + rand(0,1)*opts.sketch.x/i)+" "+ | |
(opts.y2 + rand(-1,1)*opts.sketch.y/i); | |
} | |
return sketching; | |
}; | |
/** | |
* drawing a circle shape | |
* no outline just the fill | |
* @param {object} opts - object containing the attributes | |
* @param {float|int} opts.x - x position | |
* @param {float|int} opts.y - y position | |
* @param {float|int} opts.r - radius | |
* @param {float|int} opts.angle - angle of the lines (0-360) | |
* @param {float|int} opts.density - distance between lines | |
* @param {float|int} opts.sketch - sketchiness factor | |
* @param {string} opts.shape - this is a development relic, default is "circle", alternatives "cut" and "star" | |
* @return {object} svg - d3.selection of a group object, containing the circle | |
*/ | |
sketchy.circleFill = function(opts){ | |
//merging default attributes with user attributes | |
var merged_opts = extend(defaults, opts); | |
//create a container, this is used to translate and rotate the circle, this container will be returned at the end of this function | |
var svg = merged_opts.svg.append("g").attr("transform", "translate("+merged_opts.x+" "+merged_opts.y+") rotate("+merged_opts.angle+")"); | |
var fillLines = ""; | |
//Looping through the lines | |
var y_dist = 0; | |
while(y_dist > -2*opts.r){ | |
var x; | |
//During the development i accidentaly generated those shapes and kept them :) | |
if(merged_opts.shape==="cut"){ | |
x = Math.sqrt( ( Math.pow(merged_opts.r, 2) - Math.pow((merged_opts.r-Math.abs(y_dist)), 2) ) ); | |
}else if(merged_opts.shape==="star"){ | |
x = merged_opts.r - Math.sqrt( ( Math.pow(merged_opts.r, 2) - Math.pow((merged_opts.r-Math.abs(y_dist)), 2) ) ); | |
}else{ | |
x = Math.sqrt( ( Math.pow(merged_opts.r, 2) - Math.pow((merged_opts.r-Math.abs(y_dist)), 2) ) ); | |
} | |
//Draw the sketchy lines | |
fillLines += sketchy.drawLineSinglePath({ | |
svg:svg, | |
x1:-x, | |
y1:y_dist+merged_opts.r, | |
x2:x, | |
y2:y_dist+merged_opts.r, | |
sketch:{ | |
x:merged_opts.density*merged_opts.sketch, | |
y:merged_opts.density*merged_opts.sketch | |
} | |
}); | |
y_dist -= merged_opts.density; | |
} | |
svg.append("path").attr("d", fillLines); | |
return svg; | |
}; | |
/** | |
* draws a rectangle | |
* no outline just the fill | |
* @param {object} opts - object containing the attributes | |
* @param {float|int} opts.x - x position | |
* @param {float|int} opts.y - y position | |
* @param {float|int} opts.width - width | |
* @param {float|int} opts.height - height | |
* @param {float|int} opts.angle - angle of the lines (0-360) | |
* @param {float|int} opts.density - distance between lines | |
* @param {float|int} opts.sketch - sketchiness factor | |
* @return {object} svg - d3.selection of a group object, containing the rectangle | |
*/ | |
sketchy.rectFill = function(opts){ | |
var svg = opts.svg.append("g").attr("transform", "translate("+opts.x+" "+opts.y+")"); | |
opts.svg = svg; | |
return sketchy.drawPattern(opts); | |
}; | |
/** | |
* draws a background pattern in the shape of a square according to x,y,with,height | |
* @param {object} opts - object containing the attributes | |
* @param {float|int} opts.x - x position | |
* @param {float|int} opts.y - y position | |
* @param {float|int} opts.width - width | |
* @param {float|int} opts.height - height | |
* @param {float|int} opts.angle - angle of the lines (0-360) | |
* @param {float|int} opts.density - distance between lines | |
* @param {float|int} opts.sketch - sketchiness factor | |
* @return {object} svg - d3.selection of a group object, containing the background | |
*/ | |
sketchy.drawPattern = function(opts){ | |
var svg = opts.svg; | |
var drawCode = ""; | |
//angle for strokes | |
var angle = opts.angle; | |
while(angle > 360){angle -= 360;} | |
if(angle > 180){angle -= 180;} | |
var radian = (Math.PI/180)*(90-angle); | |
var vector = { | |
y:1, | |
x:-1/Math.tan(radian) | |
}; | |
//distance between strokes | |
var dist = opts.density; | |
var vy, tx, ty, vx, y1, x1, y_dist, x_dist; | |
opts.x = 0; | |
opts.y = 0; | |
var x = opts.x, y = opts.y; | |
if(Math.abs(angle) === 90){ | |
while(y < opts.y+opts.height){ | |
drawCode += sketchy.drawLineSinglePath({ | |
svg:svg, | |
x1:x, | |
y1:y, | |
x2:x+opts.width, | |
y2:y, | |
sketch:{ | |
x:dist*opts.sketch, | |
y:dist*opts.sketch | |
} | |
}); | |
y += dist; | |
} | |
}else if((Math.abs(angle) === 180)||(angle === 0)){ | |
while(x < opts.x+opts.width){ | |
drawCode += sketchy.drawLineSinglePath({ | |
svg:svg, | |
x1:x, | |
y1:y, | |
x2:x, | |
y2:y+opts.height, | |
sketch:{ | |
x:dist*opts.sketch, | |
y:dist*opts.sketch | |
} | |
}); | |
x += dist; | |
} | |
}else if(angle < 90){ | |
y_dist = Math.abs(dist / Math.sin(Math.PI/180*angle)); | |
x_dist = Math.abs(dist / Math.sin(Math.PI/180*(90-angle))); | |
y += y_dist; | |
y1 = opts.y; | |
x1 = opts.x; | |
while(y1 < opts.y+opts.height){ | |
vx = opts.width / vector.x; | |
x1 = opts.width + x; | |
y1 = y + vector.y * vx; | |
ty = y; | |
tx = x; | |
if(y1<opts.y){ | |
vy = (y-opts.y)/vector.y; | |
x1 = x + Math.abs(vector.x) * vy; | |
y1 = opts.y; | |
}else if(y > (opts.y+opts.height)){ | |
ty = opts.y+opts.height; | |
vy = (ty-y1)/vector.y; | |
tx = x + opts.width - vy*Math.abs(vector.x); | |
} | |
drawCode += sketchy.drawLineSinglePath({ | |
svg:svg, | |
x1:tx, | |
y1:ty, | |
x2:x1, | |
y2:y1, | |
sketch:{ | |
x:x_dist*opts.sketch, | |
y:y_dist*opts.sketch | |
} | |
}); | |
y += y_dist; | |
} | |
}else{ | |
y_dist = Math.abs(dist / Math.sin(Math.PI/180*angle)); | |
x_dist = Math.abs(dist / Math.sin(Math.PI/180*(180-angle))); | |
y = opts.y+opts.height; | |
y -= y_dist; | |
y1 = opts.y+opts.height; | |
x1 = opts.x; | |
while(y1 > opts.y){ | |
vx = opts.width / vector.x; | |
x1 = opts.width + x; | |
y1 = y + vector.y * vx; | |
ty = y; | |
tx = x; | |
if(y1>(opts.y+opts.height)){ | |
vy = (y-(opts.y+opts.height))/vector.y; | |
x1 = x + Math.abs(vector.x * vy); | |
y1 = opts.y+opts.height; | |
}else if(y < opts.y){ | |
ty = opts.y; | |
vy = (ty-y1)/vector.y; | |
tx = x + opts.width - Math.abs(vy*vector.x); | |
} | |
drawCode += sketchy.drawLineSinglePath({ | |
svg:svg, | |
x1:tx, | |
y1:ty, | |
x2:x1, | |
y2:y1, | |
sketch:{ | |
x:x_dist*opts.sketch, | |
y:y_dist*opts.sketch | |
} | |
}); | |
y -= y_dist; | |
} | |
} | |
svg.append("path") | |
.attr("d", drawCode); | |
return svg; | |
}; | |
/** | |
* draws a background pattern in the shape of a square according to the position and size of the clip-path object | |
* @param {object} opts - object containing the attributes | |
* @param {string} opts.clip - id of the clip path | |
* @param {float|int} opts.angle - angle of the lines (0-360) | |
* @param {float|int} opts.density - distance between lines | |
* @param {float|int} opts.sketch - sketchiness factor | |
* @param {float|int} opts.margin - extra margin for the background | |
* @return {object} svg - d3.selection of a group object, containing the background | |
*/ | |
sketchy.fill = function(opts){ | |
var merged_opts = extend(defaults, opts); | |
var svg = merged_opts.svg.append("g") | |
.attr("clip-path", "url(#"+merged_opts.clip+")"); | |
//Get the bounding box of the object that wants a background | |
var bb = d3.select("#"+merged_opts.clip).node().getBBox(); | |
//To make sure that the background covers the whole are we increase the background by a few pixels | |
merged_opts.x = bb.x-merged_opts.margin; | |
merged_opts.y = bb.y-merged_opts.margin; | |
merged_opts.width = bb.width + 2*merged_opts.margin; | |
merged_opts.height = bb.height + 2*merged_opts.margin; | |
merged_opts.svg = svg; | |
return sketchy.drawPattern(merged_opts); | |
}; | |
/** | |
* draws a background pattern in the shape of a square according to the position and size of the clip-path object | |
* @param {object} opts - object containing the attributes | |
* @param {array} opts.path - array of points {x:float|integer, y:float|integer} | |
* @param {int} opts.count - how many altered paths should be generated | |
* @param {float|int} opts.sketch - sketchiness factor | |
* @return {array} paths - altered paths | |
*/ | |
sketchy.alterPath = function(opts){ | |
var merged_opts = extend(defaults, opts); | |
var paths = []; | |
for(var i = 0; i<merged_opts.count; i++){ | |
var t_path = []; | |
for(var j = 0; j<merged_opts.path.length; j++){ | |
t_path.push({ | |
x:merged_opts.path[j].x + rand(-1,1)*merged_opts.sketch/(i+1), | |
y:merged_opts.path[j].y + rand(-1,1)*merged_opts.sketch/(i+1) | |
}); | |
} | |
paths.push(t_path); | |
} | |
return paths; | |
}; | |
/** | |
* Draws alterPath() paths | |
* only straight lines, use alterPath and your own drawing function to draw curves etc. | |
* @param {object} opts - object containing the attributes | |
* @param {array} opts.svg - d3.selection of an svg | |
* @param {array} opts.path - array of points {x:float|integer, y:float|integer} | |
* @param {int} opts.count - how many altered paths should be generated | |
* @param {float|int} opts.sketch - sketchiness factor | |
* @return {object} svg - svg with the strokes | |
*/ | |
sketchy.pathStroke = function(opts){ | |
var paths = sketchy.alterPath(opts); | |
var svg = opts.svg; | |
var drawCode = ""; | |
for(var i = 0; i<paths.length; i++){ | |
for(var j = 0; j<paths[i].length; j++){ | |
var x1 = paths[i][j].x; | |
var y1 = paths[i][j].y, x2, y2; | |
if(j<(paths[i].length-1)){ | |
x2 = paths[i][j+1].x; | |
y2 = paths[i][j+1].y; | |
}else{ | |
x2 = paths[i][0].x; | |
y2 = paths[i][0].y; | |
} | |
drawCode += sketchy.drawLineSinglePath({ | |
svg:svg, | |
x1:x1, | |
y1:y1, | |
x2:x2, | |
y2:y2, | |
sketch:{ | |
x:opts.sketch, | |
y:opts.sketch | |
} | |
}); | |
} | |
} | |
svg.append("path") | |
.style("stroke", opts.stroke) | |
.style("stroke-width", opts.strokeWidth) | |
.attr("d", drawCode); | |
return svg; | |
}; | |
/** | |
* Helper function for circleStroke | |
* Adapted from http://codepen.io/spencerthayer/pen/nhjwu | |
* Generates an altered circle path | |
* @param {float|int} radius - radius of the circle | |
* @param {float|int} radius_min - alternating radius min | |
* @param {float|int} radius_max - alternating radius max | |
* @param {float|int} s_angle_min - alternating angle min | |
* @param {float|int} s_angle_max - alternating angle max | |
* @param {float|int} rotation_min - alternating rotation min | |
* @param {float|int} rotation_max - alternating rotation max | |
* @return {string} path - altered circle svg path | |
*/ | |
function circlePath(radius, radius_min,radius_max, s_angle_min, s_angle_max, rotation_min,rotation_max) { | |
var c = 0.551915024494, | |
b = Math.atan(c), | |
d = Math.sqrt(c*c+1*1), | |
r = radius, | |
o = rand(s_angle_min, s_angle_max)*Math.PI/180, | |
path = 'M'; | |
path += [r * Math.sin(o), r * Math.cos(o)]; | |
path += ' C' + [d * r * Math.sin(o + b), d * r * Math.cos(o + b)]; | |
for (var i=0; i<4; i++) { | |
o += Math.PI/2 * (1 + rand(rotation_min, rotation_max)); | |
r *= (1 + rand(radius_min, radius_max)); | |
path += ' ' + (i?'S':'') + [d * r * Math.sin(o - b), d * r * Math.cos(o - b)]; | |
path += ' ' + [r * Math.sin(o), r * Math.cos(o)]; | |
} | |
return path; | |
} | |
/** | |
* Helper function for circleStroke | |
* Adapted from http://codepen.io/spencerthayer/pen/nhjwu | |
* Generates the transform value for squashing and rotating | |
* @param {float|int} squash_min - squashing min | |
* @param {float|int} squash_max - squashing max | |
* @param {float|int} squash_rotation_min - squashing rotation min | |
* @param {float|int} squash_rotation_max - squashing rotation max | |
* @return {string} path - transform string | |
*/ | |
function circleTransform(squash_min, squash_max, squash_rotation_min, squash_rotation_max) { | |
var o = rand(squash_rotation_min, squash_rotation_max); | |
return 'rotate('+o+')'+'scale(1,'+rand(squash_min, squash_max) + ')'; | |
} | |
/** | |
* Draw a sketch circle stroke | |
* Adapted from http://codepen.io/spencerthayer/pen/nhjwu | |
* @param {object} opts - object containing the attributes | |
* @param {object} opts.svg - svg container | |
* @param {float|int} opts.x - center x of circle | |
* @param {float|int} opts.y - center y of circle | |
* @param {float|int} opts.r - radius of circle | |
* @param {int} count - number of strokes | |
* @param {float|int} sketch - sketchiness factor | |
* @return {object} svg - d3.selection of the svg containing the circles | |
*/ | |
sketchy.circleStroke = function(opts){ | |
var merged_opts = extend(defaults, opts); | |
var svg = merged_opts.svg.append("g").attr('transform', function() { return "translate("+merged_opts.x+" "+merged_opts.y+") "+circleTransform(1,1, 0,360); }); | |
var drawCode = ""; | |
for(var i = 0; i<merged_opts.count; i++){ | |
drawCode += " " + circlePath(merged_opts.r, merged_opts.sketch/-50/(i+1),merged_opts.sketch/10/(i+1), 200,240, 0,merged_opts.sketch/5/(i+1)); | |
} | |
svg.append('path') | |
.attr("class", "sketchy-stroke") | |
.attr('d', drawCode); | |
return svg; | |
}; | |
/** | |
* Draw a sketch rectangle stroke | |
* @param {object} opts - object containing the attributes | |
* @param {object} opts.svg - svg container | |
* @param {float|int} opts.x - x coordinate | |
* @param {float|int} opts.y - y coordinate | |
* @param {float|int} opts.width - width | |
* @param {float|int} opts.height - height | |
* @param {int} count - number of strokes | |
* @param {float|int} sketch - sketchiness factor | |
* @return {object} svg - d3.selection of the svg containing the rectangles | |
*/ | |
sketchy.rectStroke = function(opts){ | |
var merged_opts = extend(defaults, opts); | |
var svg = merged_opts.svg.append("g"); | |
var path = [ | |
{x:merged_opts.x, y:merged_opts.y}, | |
{x:merged_opts.x+merged_opts.width, y:merged_opts.y}, | |
{x:merged_opts.x+merged_opts.width, y:merged_opts.y+merged_opts.height}, | |
{x:merged_opts.x, y:merged_opts.y+merged_opts.height} | |
]; | |
return sketchy.pathStroke({svg:svg, path:path, count:merged_opts.count, sketch:merged_opts.sketch}); | |
}; | |
return sketchy; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Strange Game</title> | |
<meta charset="utf-8" /> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script src="//d3js.org/d3-geo-projection.v1.min.js"></script> | |
<script src="//d3js.org/topojson.v1.min.js"></script> | |
<script src="d3.sketchy.js"></script> | |
<script src="cheapMarky.js"></script> | |
</head> | |
<style> | |
body { | |
background: black; | |
} | |
svg { | |
height: 1000px; | |
width: 1280px; | |
border: none; | |
} | |
.countries { | |
fill: $443636; | |
stroke: none; | |
} | |
</style> | |
<body> | |
<div id="viz"> | |
<svg> | |
</svg> | |
</div> | |
<div id="controls" /> | |
</body> | |
<footer> | |
<script> | |
var colorScale = d3.scaleOrdinal().range(["#f8a313", "#a5c4c5", "#bab218", "#f0d976", "#e8381b", "#ee791e"]); | |
var filter = d3.select("svg").append("defs").append("filter").attr("id", "gooeyCodeFilter"); | |
filter.append("feGaussianBlur").attr("id", "gaussblurrer").attr("in", "SourceGraphic").attr("stdDeviation", 4).attr("color-interpolation-filters", "sRGB").attr("result", "blur"); | |
filter.append("feColorMatrix").attr("in", "blur").attr("mode", "matrix").attr("values", "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 34 -7").attr("result", "gooey"); | |
var svg = d3.select("svg"); | |
d3.select("svg").append("g").attr("class", "bg") | |
d3.select("svg").append("g").attr("class", "fill") | |
.style("filter", "url(#gooeyCodeFilter)") | |
d3.select("svg").append("g").attr("class", "border") | |
d3.select("svg").append("g").attr("class", "explosions") | |
.style("filter", "url(#gooeyCodeFilter)") | |
var sketchy = d3sketchy(); | |
var projection = d3.geoArmadillo() | |
.scale(300) | |
.translate([600, 360]) | |
.parallel(20) | |
.rotate([-12, 0]) | |
.precision(.1); | |
var geoPath = d3.geoPath().projection(projection); | |
var line = d3.line() | |
.x(function(d) { return d.x; }) | |
.y(function(d) { return d.y; }); | |
var numSides = 6 | |
var sources | |
var countries | |
var sideHash | |
d3.json("simpleworld.json", createMap); | |
var powers = ["USA", "RUS", "CHN", "ISR", "IND", "PAK", "GBR", "FRA"] | |
function createMap(world) { | |
countries = topojson.feature(world, world.objects.world); | |
renderMap(countries, projection) | |
} | |
function renderMap(countries, projection) { | |
sideHash = {} | |
countries.features = countries.features.filter(function (d) {return d.id !== "ATA"}) | |
countries.features.forEach(function (d) { | |
sideHash[d.id] = parseInt(Math.random() * numSides) | |
}) | |
sources = countries.features.filter(function (p) {return powers.indexOf(p.id) !== -1}) | |
d3.select("g.bg") | |
.selectAll("path") | |
.data(countries.features) | |
.enter() | |
.append("path") | |
.attr("d", geoPath) | |
.attr("class", "countries") | |
.each(function (d, i) { | |
d.centroid = geoPath.centroid(d) | |
var originalNode = this; | |
d.geometry.coordinates.forEach(function (p) { | |
if (p.length < 2) { | |
p = p[0]; | |
} | |
var projectedArea = []; | |
p.forEach(function (coordinate) { | |
var proj = projection(coordinate); | |
projectedArea.push({x: proj[0], y: proj[1]}); | |
}) | |
sketchy.pathStroke({svg: d3.select("g.border"), path:projectedArea, density:3, sketch:2, stroke: colorScale(sideHash[d.id]), strokeWidth: "2px"}); | |
}) | |
}) | |
d3.select("g.fill").selectAll("path") | |
.attr("stroke-dasharray", function () {return "0," + this.getTotalLength()}) | |
.transition() | |
.delay(function (d,i) {return i * 10}) | |
.transition() | |
.duration(500) | |
.attrTween("stroke-dasharray", tweenDash); | |
d3.select("g.border").selectAll("path") | |
.style("fill", "none") | |
.attr("stroke-dasharray", function () {return "0," + this.getTotalLength()}) | |
.transition() | |
.delay(function (d,i) {return i * 10}) | |
.transition() | |
.duration(500) | |
.attrTween("stroke-dasharray", tweenDash); | |
nuke() | |
} | |
function nuke() { | |
d3.select("svg") | |
.selectAll("path.nukelines") | |
.data(d3.range(100)) | |
.enter() | |
.append("path") | |
.attr("class", "nukelines") | |
.each(function (d,i) { | |
var p1 = parseInt(Math.random() * sources.length) | |
var targets = countries.features.filter(function (p) {return sideHash[p.id] !== sideHash[sources[p1].id]}) | |
var p2 = parseInt(Math.random() * targets.length) | |
var x1 = sources[p1].centroid[0] + (Math.random() * 30) - 15 | |
var y1 = sources[p1].centroid[1] | |
var x2 = targets[p2].centroid[0] + (Math.random() * 50) - 25 | |
var y2 = targets[p2].centroid[1] + (Math.random() * 50) - 25 | |
var delay = i < 10 ? 2000 + i * 150 : 3500 + i * 10 | |
d3.select(this) | |
.attr("d", "M" + x1 + "," + y1 + " S" + ((x1 + x2)/2) + "," + ((y1 + y2)/2 - 200) + " " + x2 + "," + y2) | |
.style("fill", "none") | |
.style("stroke", d3.rgb(colorScale(sideHash[sources[p1].id])).brighter(.25)) | |
.style("stroke-width", 1) | |
.style("stroke-dasharray", "0 " + this.getTotalLength()) | |
.transition() | |
.delay(delay) | |
.transition() | |
.duration(1000) | |
.style("stroke-dasharray", this.getTotalLength() + " 0") | |
createExplosion(x2, y2, delay + 500, d3.rgb(colorScale(sideHash[sources[p1].id])).brighter(.5)) | |
}) | |
setTimeout(function(){ d3.selectAll("g.blast").remove(); d3.selectAll("path.nukelines").remove(); nuke() }, 8000); | |
} | |
function tweenDash() { | |
var l = this.getTotalLength(), | |
i = d3.interpolateString("0," + l, l + "," + l); | |
return function(t) { return i(t); }; | |
} | |
function cheapSketchy(path) { | |
var length = path.getTotalLength(); | |
var drawCode = ""; | |
var x = 0; | |
var step = 2.5; | |
while (x < length / 2) { | |
var start = path.getPointAtLength(x); | |
var end = path.getPointAtLength(length - x); | |
drawCode += " M" + (start.x + (Math.random() * step - step/2)) + " " + (start.y + (Math.random() * step - step/2)) + "L" + (end.x + (Math.random() * step - step/2)) + " " + (end.y + (Math.random() * step - step/2)); | |
x += step + (Math.random() * step); | |
} | |
return drawCode; | |
} | |
function createExplosion(x, y, delay, color) { | |
var explosionG = d3.select("g.explosions") | |
.append("g") | |
.attr("class", "blast") | |
.attr("transform", "translate(" + x + "," + y + ")" ) | |
.style("filter", "url(#gooeyCodeFilter)") | |
explosionG | |
.append("path") | |
.style("fill", "none") | |
.style("stroke", "none") | |
.attr("d", circlePath(20)) | |
explosionG | |
.each(function (d,i) { | |
var artCircles = cheapPopArtsy(d3.select(this).select("path").node()) | |
d3.select(this) | |
.selectAll("circle") | |
.data(artCircles) | |
.enter() | |
.append("circle") | |
.attr("r", 0) | |
.style("fill", color) | |
}) | |
var artBarTransition = explosionG | |
.transition() | |
.delay(delay) | |
artBarTransition | |
.selectAll("circle") | |
.transition() | |
.delay(function (d) {return Math.abs(d[0] + d[1])}) | |
.transition() | |
.duration(100) | |
.attr("r", function () {return Math.random() * 5}) | |
.attr("cx", function (d) {return d[0]}) | |
.attr("cy", function (d) {return d[1]}) | |
} | |
</script> | |
</footer> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment