Some random stuff I was messing around with
Last active
November 29, 2015 22:02
-
-
Save chronick/27419128b43711836fed to your computer and use it in GitHub Desktop.
This file contains hidden or 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
svg = d3.select('svg') | |
width = svg.node().getBoundingClientRect().width | |
height = svg.node().getBoundingClientRect().height | |
vis = svg.append('g') | |
.attr | |
transform: "translate(#{width/2},#{height/2})" | |
# [x, y, z] -> [-Math.sqrt(3)/2*x+Math.sqrt(3)/2*y, 0.5*x+0.5*y-z] | |
isometric = (_3d_p) -> [-Math.sqrt(3)/2*_3d_p[0]+Math.sqrt(3)/2*_3d_p[1], +0.5*_3d_p[0]+0.5*_3d_p[1]-_3d_p[2]] | |
parallelepipedon = (d) -> | |
d.x = 0 if not d.x? | |
d.y = 0 if not d.y? | |
d.z = 0 if not d.z? | |
d.dx = 10 if not d.dx? | |
d.dy = 10 if not d.dy? | |
d.dz = 10 if not d.dz? | |
fb = isometric [d.x, d.y, d.z], | |
mlb = isometric [d.x+d.dx, d.y, d.z], | |
nb = isometric [d.x+d.dx, d.y+d.dy, d.z], | |
mrb = isometric [d.x, d.y+d.dy, d.z], | |
ft = isometric [d.x, d.y, d.z+d.dz], | |
mlt = isometric [d.x+d.dx, d.y, d.z+d.dz], | |
nt = isometric [d.x+d.dx, d.y+d.dy, d.z+d.dz], | |
mrt = isometric [d.x, d.y+d.dy, d.z+d.dz] | |
d.iso = { | |
face_bottom: [fb, mrb, nb, mlb], | |
face_left: [mlb, mlt, nt, nb], | |
face_right: [nt, mrt, mrb, nb], | |
face_top: [ft, mrt, nt, mlt], | |
outline: [ft, mrt, mrb, nb, mlb, mlt] | |
far_point: fb # used to control the z-index of iso objects | |
} | |
return d | |
iso_layout = (data, shape, scale) -> | |
scale = 1 if not scale? | |
data.forEach (d) -> | |
shape(d, scale) | |
# data must be drawn from farthest to nearest | |
data.sort (a,b) -> a.iso.far_point[1] - b.iso.far_point[1] | |
path_generator = (d) -> 'M' + d.map((p)->p.join(' ')).join('L') + 'z' | |
y_color = d3.scale.category10() | |
L = 30 | |
PAD = 6 | |
data = d3.range(6*6).map (i) -> { | |
x: (i%6)*L, | |
y: Math.floor(i/6)*L, | |
dx: L-PAD, | |
dy: L-PAD, | |
dz: 10+Math.random()*6*L | |
} | |
iso_layout(data, parallelepipedon) | |
pipedons = vis.selectAll('.pipedon') | |
.data(data) | |
enter_pipedons = pipedons.enter().append('g') | |
.attr | |
class: 'pipedon' | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face bottom' | |
d: (d) -> path_generator(d.iso.face_bottom) | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face left template' | |
d: (d) -> path_generator(d.iso.face_left) | |
fill: (d) -> | |
# color the template face according to y | |
return y_color(d.y) | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face right' | |
d: (d) -> path_generator(d.iso.face_right) | |
fill: (d) -> | |
# right face is darker than the template (left face) | |
color = d3.hcl(d3.select(this.parentNode).select('.template').style('fill')) | |
return d3.hcl(color.h, color.c, color.l-12) | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face top' | |
d: (d) -> path_generator(d.iso.face_top) | |
fill: (d) -> | |
# right face is brighter than the template (left face) | |
color = d3.hcl(d3.select(this.parentNode).select('.template').style('fill')) | |
return d3.hcl(color.h, color.c, color.l+12) | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso outline' | |
d: (d) -> path_generator(d.iso.outline) |
This file contains hidden or 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
.iso.face.bottom { | |
display: none; | |
} | |
.iso.outline { | |
stroke: white; | |
fill: none; | |
} |
This file contains hidden or 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Isometric Projection</title> | |
<link type="text/css" href="index.css" rel="stylesheet"/> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
</head> | |
<body> | |
<svg width="960px" height="500px"></svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains hidden or 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() { | |
var data, enter_pipedons, height, iso_layout, isometric, parallelepipedon, path_generator, pipedons, svg, vis, width, y_color; | |
// COLORS = ['hsl(10, 60%, 50%)', 'hsl(15, 90%, 60%)', 'hsl(30, 90%, 60%)', 'hsl(48, 99%, 70%)']; | |
COLORS = ['#24806C', '#30ACB9', '#49C983', '#B8E7B8']; | |
// COLORS = ['#359447']; | |
var CELL_SIZE = 30; | |
var PAD = 1; | |
var SIZE = 14; | |
var HEIGHT = 20; | |
var PASSES = 2; | |
function numericSort(array) { | |
return array | |
// ensure the array is changed in-place | |
.slice() | |
// comparator function that treats input as numeric | |
.sort(function(a, b) { | |
return a - b; | |
}); | |
} | |
function mode(x) { | |
// Handle edge cases: | |
// The median of an empty list is null | |
if (x.length === 0) { return null; } | |
else if (x.length === 1) { return x[0]; } | |
// Sorting the array lets us iterate through it below and be sure | |
// that every time we see a new number it's new and we'll never | |
// see the same number twice | |
var sorted = numericSort(x); | |
// This assumes it is dealing with an array of size > 1, since size | |
// 0 and 1 are handled immediately. Hence it starts at index 1 in the | |
// array. | |
var last = sorted[0], | |
// store the mode as we find new modes | |
value, | |
// store how many times we've seen the mode | |
maxSeen = 0, | |
// how many times the current candidate for the mode | |
// has been seen | |
seenThis = 1; | |
// end at sorted.length + 1 to fix the case in which the mode is | |
// the highest number that occurs in the sequence. the last iteration | |
// compares sorted[i], which is undefined, to the highest number | |
// in the series | |
for (var i = 1; i < sorted.length + 1; i++) { | |
// we're seeing a new number pass by | |
if (sorted[i] !== last) { | |
// the last number is the new mode since we saw it more | |
// often than the old one | |
if (seenThis > maxSeen) { | |
maxSeen = seenThis; | |
value = last; | |
} | |
seenThis = 1; | |
last = sorted[i]; | |
// if this isn't a new number, it's one more occurrence of | |
// the potential mode | |
} else { seenThis++; } | |
} | |
return value; | |
} | |
svg = d3.select('svg'); | |
width = svg.node().getBoundingClientRect().width; | |
height = svg.node().getBoundingClientRect().height; | |
vis = svg.append('g').attr({ | |
transform: "translate(" + (width / 2) + "," + 20 + ")" | |
}); | |
isometric = function(_3d_p) { | |
return [ | |
-Math.sqrt(3) / 2 * _3d_p[0] + Math.sqrt(3) / 2 * _3d_p[1], | |
(+0.5 * _3d_p[0] + 0.5 * _3d_p[1] - _3d_p[2]) * 0.85 | |
]; | |
}; | |
parallelepipedon = function(d) { | |
if (!(d.x != null)) { | |
d.x = 0; | |
} | |
if (!(d.y != null)) { | |
d.y = 0; | |
} | |
if (!(d.z != null)) { | |
d.z = 0; | |
} | |
if (!(d.dx != null)) { | |
d.dx = 10; | |
} | |
if (!(d.dy != null)) { | |
d.dy = 10; | |
} | |
if (!(d.dz != null)) { | |
d.dz = 10; | |
} | |
var mrt = isometric([d.x, d.y + d.dy, d.z + d.dz]), | |
nt = isometric([d.x + d.dx, d.y + d.dy, d.z + d.dz]), | |
mlt = isometric([d.x + d.dx, d.y, d.z + d.dz]), | |
ft = isometric([d.x, d.y, d.z + d.dz]), | |
mrb = isometric([d.x, d.y + d.dy, d.z]), | |
nb = isometric([d.x + d.dx, d.y + d.dy, d.z]), | |
mlb = isometric([d.x + d.dx, d.y, d.z]), | |
fb = isometric([d.x, d.y, d.z]); | |
d.iso = { | |
face_bottom: [fb, mrb, nb, mlb], | |
face_left: [mlb, mlt, nt, nb], | |
face_right: [nt, mrt, mrb, nb], | |
face_top: [ft, mrt, nt, mlt], | |
outline: [ft, mrt, mrb, nb, mlb, mlt], | |
far_point: fb | |
}; | |
return d; | |
}; | |
iso_layout = function(data, shape, scale) { | |
if (!(scale != null)) { | |
scale = 1; | |
} | |
return data | |
.map(function(d) { | |
return shape(d, scale); | |
}) | |
.sort(function(a, b) { | |
return a.iso.far_point[1] - b.iso.far_point[1]; | |
}); | |
}; | |
path_generator = function(d) { | |
return 'M' + d.map(function(p) { | |
return p.join(' '); | |
}).join('L') + 'z'; | |
}; | |
color = function(d) { | |
var x = d.x / CELL_SIZE; | |
var y = d.y / CELL_SIZE; | |
var what = d3.scale.quantize() | |
.domain([3,0]) | |
.range(COLORS)(d.c); | |
return what; | |
}; //d3.scale.category10(); | |
var colorMarchingSquares = function(e, y, arr) { | |
return e.map(function(d, x) { | |
return mode([ | |
arr[(y + 1) % SIZE][x], | |
arr[(y - 1 + SIZE) % SIZE][x], | |
arr[y][(x + 1) % SIZE], | |
arr[y][(x - 1 + SIZE) % SIZE], | |
arr[(y + 1) % SIZE][(x + 1) % SIZE], | |
arr[(y + 1) % SIZE][(x - 1 + SIZE) % SIZE], | |
arr[(y - 1 + SIZE) % SIZE][(x + 1) % SIZE], | |
arr[(y - 1 + SIZE) % SIZE][(x - 1 + SIZE) % SIZE] | |
]) | |
}) | |
} | |
var colorTranslate = function(direction) { | |
return function(e, y, arr) { | |
return e.map(function(d, x) { | |
switch (direction) { | |
case 'top': | |
return arr[(y + 1) % SIZE][x]; | |
case 'bottom': | |
return arr[(y - 1 + SIZE) % SIZE][x]; | |
case 'right': | |
return arr[y][(x + 1) % SIZE]; | |
case 'left': | |
return arr[y][(x - 1 + SIZE) % SIZE]; | |
default: | |
return arr[y][x]; | |
} | |
}) | |
} | |
} | |
var colorData; | |
// Manual color data | |
// colorData = [ | |
// [1,1,1,1,2,3,3,3,2,1,1,1,1,1], | |
// [1,1,1,1,2,3,3,2,1,1,1,1,1,1], | |
// [1,1,2,2,2,3,3,2,2,1,1,1,1,1], | |
// [1,1,2,3,3,3,3,3,2,1,1,1,1,1], | |
// [1,1,2,2,3,2,2,3,2,1,1,1,1,1], | |
// [1,2,2,2,2,2,2,2,2,2,2,1,1,1], | |
// [1,1,1,2,3,3,3,3,3,2,2,1,1,1], | |
// [2,3,3,3,3,3,3,2,3,2,1,1,1,1], | |
// [1,2,2,3,3,3,2,2,2,1,1,1,1,1], | |
// [1,2,2,3,3,3,1,2,2,1,1,1,1,1], | |
// [2,2,2,3,3,3,1,1,1,1,1,1,1,1], | |
// [2,2,3,3,1,1,1,1,1,1,1,1,1,1], | |
// [2,2,2,2,2,2,1,1,1,1,1,1,1,1], | |
// [1,1,1,2,2,2,1,1,1,1,1,1,1,1] | |
// ]; | |
// var SIZE = colorData.length; | |
// Randomly generate grid palette values | |
colorData = d3.range(SIZE).map(function(i) { | |
return d3.range(SIZE).map(function(j) { | |
return Math.floor(Math.random() * 4); | |
}); | |
}); | |
// Pass through marching squares to create continuous clusters | |
colorData = d3.range(PASSES).reduce(function(accum) { | |
return accum.map(colorMarchingSquares); | |
}, colorData) | |
var fn = function(colorData) { | |
// Generate data array for path generation | |
data = d3.range(SIZE * SIZE).map(function(i) { | |
var x = (i % SIZE); | |
var y = Math.floor(i / SIZE); | |
return { | |
x: x * CELL_SIZE, | |
y: y * CELL_SIZE, | |
dx: CELL_SIZE - PAD, | |
dy: CELL_SIZE - PAD, | |
c: colorData[y][x], | |
dz: HEIGHT//10 + Math.random() * SIZE * L | |
}; | |
}); | |
pipedons = vis.selectAll('.pipedon') | |
.data(iso_layout(data, parallelepipedon)); | |
pipedons.select('path.iso.face.bottom').attr({ | |
d: function(d) { | |
return path_generator(d.iso.face_bottom); | |
} | |
}) | |
pipedons.select('path.iso.face.top').attr({ | |
d: function(d) { | |
return path_generator(d.iso.face_top); | |
}, | |
fill: color | |
}); | |
pipedons.select('path.iso.face.right').attr({ | |
d: function(d) { | |
return path_generator(d.iso.face_right); | |
}, | |
fill: function(d) { | |
var color; | |
color = d3.hcl(d3.select(this.parentNode).select('.template').style('fill')); | |
return d3.hcl(color.h, color.c, color.l + 12); | |
} | |
}); | |
pipedons.select('path.iso.face.left').attr({ | |
d: function(d) { | |
return path_generator(d.iso.face_left); | |
}, | |
fill: function(d) { | |
var color; | |
color = d3.hcl(d3.select(this.parentNode).select('.template').style('fill')); | |
return d3.hcl(color.h, color.c, color.l - 24); | |
} | |
}); | |
enter_pipedons = pipedons.enter().append('g').attr({ | |
"class": 'pipedon' | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face bottom', | |
d: function(d) { | |
return path_generator(d.iso.face_bottom); | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face top template', | |
d: function(d) { | |
return path_generator(d.iso.face_top); | |
}, | |
fill: color | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face left', | |
d: function(d) { | |
return path_generator(d.iso.face_left); | |
}, | |
fill: function(d) { | |
var color; | |
color = d3.hcl(d3.select(this.parentNode).select('.template').style('fill')); | |
return d3.hcl(color.h, color.c, color.l - 24); | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face right', | |
d: function(d) { | |
return path_generator(d.iso.face_right); | |
}, | |
fill: function(d) { | |
var color; | |
color = d3.hcl(d3.select(this.parentNode).select('.template').style('fill')); | |
return d3.hcl(color.h, color.c, color.l + 12); | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso outline', | |
d: function(d) { | |
return path_generator(d.iso.outline); | |
} | |
}); | |
}; | |
fn(colorData); | |
setInterval(function(){ | |
colorData = colorData | |
.map(colorTranslate('top')) | |
// .map(colorMarchingSquares); | |
fn(colorData); | |
}, 100) | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment