Skip to content

Instantly share code, notes, and snippets.

@mohamedkhanafer
Created February 8, 2020 12:56
Show Gist options
  • Select an option

  • Save mohamedkhanafer/7ebe075a93f27af7d5cb60595ca3bc39 to your computer and use it in GitHub Desktop.

Select an option

Save mohamedkhanafer/7ebe075a93f27af7d5cb60595ca3bc39 to your computer and use it in GitHub Desktop.
music data trial
license: mit
<!DOCTYPE html>
<head>
<title>Time Use Simulation</title>
<link rel="stylesheet" href="style/style.css" type="text/css" media="screen" />
<link rel="stylesheet" type="text/css" href="//cloud.typography.com/7626174/696048/css/fonts.css" />
<link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
</head>
<meta charset="utf-8">
<style>
/*Defining text stylings*/
#h1 {
font-size: 32px;
margin: 0px 0px 2px 0px;
font-family: 'Barlow', sans-serif;
font-weight: 300;
font-variant: small-caps slashed-zero;
color: #f20675;
}
#h2 {
font-size: 18px;
margin: 0px 0px 0px 0px;
font-family: 'Barlow', sans-serif;
font-weight: 100;
font-variant: small-caps slashed-zero;
color: #1ce3cd;
}
#link {
font-family: 'Barlow', sans-serif;
font-size: 5px;
font-variant: small-caps slashed-zero;
margin: 10px 10px 2px 0px;
color: #cecece;
}
a:link, a:visited, a:active {
text-decoration: none;
color: #cccccc;
border-bottom: 0.7px dotted #cccccc;
}
body {
background-color:#ffffff;
}
</style>
<body>
<!-- Connecting with D3 library-->
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<div id="main-wrapper">
<div id="sidebar">
<div id="current_time">Pre-baby</div>
<div id="speed">
<div class="togglebutton slow current" data-val="slow">Slow</div>
<div class="togglebutton medium" data-val="medium">Medium</div>
<div class="togglebutton fast" data-val="fast">Fast</div>
<div class="clr"></div>
</div>
<div id="note"></div>
<div id="cite">
This is an animation to highlight the different activities done by women before and after they give birth.
</div>
</div>
<div id="chart"></div>
<div class="clr"></div>
</div>
<!-- Connecting with D3 library-->
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var USER_SPEED = "slow";
var width = 780,
height = 800,
padding = 1,
maxRadius = 3;
// color = d3.scale.category10();
var sched_objs = [],
curr_minute = 0;
var act_codes = [
{"index": "1", "short": "Rock", "desc": "Rock"},
{"index": "2", "short": "Blues", "desc": "Blues"},
{"index": "3", "short": "Electronic/Dance", "desc": "Electronic/Dance"},
{"index": "4", "short": "Folk", "desc": "Folk"},
{"index": "5", "short": "Disco", "desc": "Disco"},
{"index": "6", "short": "Hip-Hop", "desc": "Hip-Hop"},
{"index": "7", "short": "Pop", "desc": "Pop"},
{"index": "8", "short": "R&B", "desc": "R&B"},
{"index": "9", "short": "Soul", "desc": "Soul"},
{"index": "10", "short": "Swing", "desc": "Swing"}
];
/// here we are defining how fast are the 3 different options:
var speeds = { "slow": 120, "medium": 90, "fast": 70 };
var time_notes = [
{ "start_minute": 1, "stop_minute": 60, "note": "The animation starts with data based on a graph by @beeonaposy." },
{ "start_minute": 90, "stop_minute": 180, "note": "In the pre-baby phase, the routine is normally split between work, sleep, and free time." },
{ "start_minute": 240, "stop_minute": 320, "note": "The baby has arrived!" },
{ "start_minute": 360, "stop_minute": 440, "note": " The typical day activities are now more devoted to the baby." },
{ "start_minute": 480, "stop_minute": 540, "note": "In this month, the baby requires a little less nursing." },
{ "start_minute": 580, "stop_minute": 640, "note": "Do not forget that in Month 1 and 2, the mother is on maternal leave." },
{ "start_minute": 720, "stop_minute": 780, "note": "Time to get back to work." },
{ "start_minute": 850, "stop_minute": 910, "note": "Older baby, a bit more free time." },
{ "start_minute": 960, "stop_minute": 1030, "note": "Back to a routine with a baby in the equation." },
];
var notes_index = 0;
// Activity to put in center of circle arrangement
var center_act = "Rock",
center_pt = { "x": 380, "y": 365 };
// Coordinates for activities
var foci = {};
act_codes.forEach(function(code, i) {
if (code.desc == center_act) {
foci[code.index] = center_pt;
} else {
var theta = 2 * Math.PI / (act_codes.length-1);
foci[code.index] = {x: 250 * Math.cos(i * theta)+380, y: 250 * Math.sin(i * theta)+365 };
}
});
// Start the SVG
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height);
//Data loading
d3.csv('https://gist.githubusercontent.com/mohamedkhanafer/db3a4f772f51fc5a8c8c5cca6bf14c05/raw/07e8b4a39ae111f790c597f8524810fbcb1fda3f/billboard1', function(data) {
data.forEach(function(d) {
var day_array = d.day.split(",");
var activities = [];
for (var i=0; i < day_array.length; i++) {
// Duration
if (i % 2 == 1) {
activities.push({'act': day_array[i-1], 'duration': +day_array[i]});
}
}
sched_objs.push(activities);
});
// Used for percentages by minute
var act_counts = {"1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "6":0,"7":0,"8":0,"9":0,"10":0,};
// A node for each person's schedule
var nodes = sched_objs.map(function(o,i) {
var act = o[0].act;
act_counts[act] += 1;
var init_x = foci[act].x + Math.random();
var init_y = foci[act].y + Math.random();
return {
act: act,
radius: 6,
x: init_x,
y: init_y,
color: color(act),
moves: 0,
next_move_time: o[0].duration,
sched: o,
}
});
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
// .links([])
.gravity(0)
.charge(0)
.friction(0.9)
.on("tick", tick)
.start();
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", function(d) { return d.radius; })
.style("fill", function(d) { return d.color; });
// .call(force.drag);
// Activity labels
var label = svg.selectAll("text")
.data(act_codes)
.enter().append("text")
.attr("class", "actlabel")
.attr("x", function(d, i) {
if (d.desc == center_act) {
return center_pt.x;
} else {
var theta = 2 * Math.PI / (act_codes.length-1);
return 340 * Math.cos(i * theta)+380;
}
})
.attr("y", function(d, i) {
if (d.desc == center_act) {
return center_pt.y;
} else {
var theta = 2 * Math.PI / (act_codes.length-1);
return 340 * Math.sin(i * theta)+365;
}
});
label.append("tspan")
.attr("x", function() { return d3.select(this.parentNode).attr("x"); })
// .attr("dy", "1.3em")
.attr("text-anchor", "middle")
.text(function(d) {
return d.short;
});
label.append("tspan")
.attr("dy", "1.3em")
.attr("x", function() { return d3.select(this.parentNode).attr("x"); })
.attr("text-anchor", "middle")
.attr("class", "actpct")
.text(function(d) {
return act_counts[d.index] + "%";
});
// Update nodes based on activity and duration
function timer() {
d3.range(nodes.length).map(function(i) {
var curr_node = nodes[i],
curr_moves = curr_node.moves;
// Time to go to next activity
if (curr_node.next_move_time == curr_minute) {
if (curr_node.moves == curr_node.sched.length-1) {
curr_moves = 0;
} else {
curr_moves += 1;
}
// Subtract from current activity count
act_counts[curr_node.act] -= 1;
// Move on to next activity
curr_node.act = curr_node.sched[ curr_moves ].act;
// Add to new activity count
act_counts[curr_node.act] += 1;
curr_node.moves = curr_moves;
curr_node.cx = foci[curr_node.act].x;
curr_node.cy = foci[curr_node.act].y;
nodes[i].next_move_time += nodes[i].sched[ curr_node.moves ].duration;
}
});
force.resume();
curr_minute += 1;
// Update percentages
label.selectAll("tspan.actpct")
.text(function(d) {
return readablePercent(act_counts[d.index]);
});
// Update time
var true_minute = curr_minute % 1440;
d3.select("#current_time").text(minutesToTime(true_minute));
// Update notes
// var true_minute = curr_minute % 1440;
if (true_minute == time_notes[notes_index].start_minute) {
d3.select("#note")
.style("top", "0px")
.transition()
.duration(600)
.style("top", "20px")
.style("color", "#000000")
.text(time_notes[notes_index].note);
}
// Make note disappear at the end.
else if (true_minute == time_notes[notes_index].stop_minute) {
d3.select("#note").transition()
.duration(1000)
.style("top", "300px")
.style("color", "#ffffff");
notes_index += 1;
if (notes_index == time_notes.length) {
notes_index = 0;
}
}
setTimeout(timer, speeds[USER_SPEED]);
}
setTimeout(timer, speeds[USER_SPEED]);
function tick(e) {
var k = 0.04 * e.alpha;
// Push nodes toward their designated focus.
nodes.forEach(function(o, i) {
var curr_act = o.act;
// Make sleep more sluggish moving.
if (curr_act == "0") {
var damper = 0.9;
} else {
var damper = 0.9;
}
o.color = color(curr_act);
o.y += (foci[curr_act].y - o.y) * k * damper;
o.x += (foci[curr_act].x - o.x) * k * damper;
});
circle
.each(collide(.5))
.style("fill", function(d) { return d.color; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
// Resolve collisions between nodes.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + maxRadius + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.act !== quad.point.act) * padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
// Speed toggle
d3.selectAll(".togglebutton")
.on("click", function() {
if (d3.select(this).attr("data-val") == "slow") {
d3.select(".slow").classed("current", true);
d3.select(".medium").classed("current", false);
d3.select(".fast").classed("current", false);
} else if (d3.select(this).attr("data-val") == "medium") {
d3.select(".slow").classed("current", false);
d3.select(".medium").classed("current", true);
d3.select(".fast").classed("current", false);
}
else {
d3.select(".slow").classed("current", false);
d3.select(".medium").classed("current", false);
d3.select(".fast").classed("current", true);
}
USER_SPEED = d3.select(this).attr("data-val");
});
}); // @end d3.tsv
function color(activity) {
var colorByActivity = {
"0": "#e0d400",
"1": "#1c8af9",
"2": "#51BC05",
"3": "#FF7F00",
"4": "#DB32A4",
"5": "#00CDF8",
"6": "#E63B60",
"7": "#8E5649",
"8": "#68c99e",
"9": "#a477c8",
"10": "#5C76EC",
"11": "#E773C3",
"12": "#799fd2",
"13": "#038a6c",
"14": "#cc87fa",
"15": "#ee8e76",
"16": "#bbbbbb",
}
return colorByActivity[activity];
}
var d3Graph = d3.select('svg')
var dropShadowFilter = d3Graph.append('svg:filter')
.attr('id', 'drop-shadow')
.attr('filterUnits', "userSpaceOnUse")
.attr('width', '250%')
.attr('height', '250%');
dropShadowFilter.append('svg:feGaussianBlur')
.attr('in', 'SourceGraphic')
.attr('stdDeviation', 2)
.attr('result', 'blur-out');
dropShadowFilter.append('svg:feColorMatrix')
.attr('in', 'blur-out')
.attr('type', 'hueRotate')
.attr('values', 180)
.attr('result', 'color-out');
dropShadowFilter.append('svg:feOffset')
.attr('in', 'color-out')
.attr('dx', 3)
.attr('dy', 3)
.attr('result', 'the-shadow');
dropShadowFilter.append('svg:feBlend')
.attr('in', 'SourceGraphic')
.attr('in2', 'the-shadow')
.attr('mode', 'normal');
var simpleGradient = d3Graph.append('defs')
.append('radialGradient')
.attr('id', 'fake-shadow');
simpleGradient.append('stop')
.attr('offset', "80%")
.attr('stop-color', '#01AFAF');
simpleGradient.append('stop')
.attr('offset', "100%")
.attr('stop-color', "#01AFAF00");
// Output readable percent based on count.
function readablePercent(n) {
var pct = 100 * n / 100;
if (pct < 1 && pct > 0) {
pct = "<1%";
} else {
pct = Math.round(pct) + "%";
}
return pct;
}
// Minutes to time of day. Data is minutes from 4am.
function minutesToTime(m) {
var minutes = (m + 4*60) % 1440;
var hh = Math.floor(minutes / 60);
var ampm;
if (hh > 12) {
hh = hh - 12;
ampm = "pm";
} else if (hh == 12) {
ampm = "pm";
} else if (hh == 0) {
hh = 12;
ampm = "am";
} else {
ampm = "am";
}
var mm = minutes % 60;
if (mm < 10) {
mm = "0" + mm;
}
run=hh + ampm
if(run=="4am" || run=="5am" || run=="6am"|| run=="7am")
return "Pre-pregnancy"
else if (run=="8am" || run=="9am" || run=="10am" ||run=="11am")
return "1 Month old Baby";
else if (run=="12pm" || run=="1pm" || run=="2pm" || run=="3pm")
return "2 Months old Baby";
else if (run=="4pm" || run=="5pm" || run=="6pm" || run=="7pm")
return "3 Months old Baby";
else if (run=="8pm" || run=="9pm" || run=="10pm" || run=="11pm")
return "4 Months old Baby";
else if (run=="12am" || run=="1am" || run=="2am" || run=="3am")
return "5-6 Months old Baby"
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment