|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
|
<meta charset="utf-8"> |
|
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> |
|
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> |
|
|
|
<link rel="stylesheet" type="text/css" href="theme.css"></link> |
|
</head> |
|
<body> |
|
|
|
<div id="main"> |
|
<p> |
|
Click the plus and minus buttons to adjust the variables |
|
</p> |
|
<div class="variables"> |
|
<div id="wages_par" class="variable"> |
|
Wages |
|
<div id="wage_div"> |
|
</div> |
|
<button onclick="increase('wage')">+</button> |
|
<button onclick="decrease('wage')">-</button> |
|
</div> |
|
<div id="tax_par" class="variable"> |
|
Tax |
|
<div id="tax_div"> |
|
</div> |
|
<button onclick="increase('tax')">+</button> |
|
<button onclick="decrease('tax')">-</button> |
|
</div> |
|
<div id="c_pref_par" class="variable"> |
|
Consumption Preference |
|
<div id="c_pref_div"> |
|
</div> |
|
<button onclick="increase('c_pref')">+</button> |
|
<button onclick="decrease('c_pref')">-</button> |
|
</div> |
|
<div id="l_pref_par" class="variable"> |
|
Leisure Preference |
|
<div id="l_pref_div"> |
|
</div> |
|
<button onclick="increase('l_pref')">+</button> |
|
<button onclick="decrease('l_pref')">-</button> |
|
</div> |
|
<div id="c_con_par" class="variable"> |
|
Consumption Concavity |
|
<div id="c_con_div"> |
|
</div> |
|
<button onclick="increase('c_con')">+</button> |
|
<button onclick="decrease('c_con')">-</button> |
|
</div> |
|
<div id="l_con_par" class="variable"> |
|
Leisure Concavity |
|
<div id="l_con_div"> |
|
</div> |
|
<button onclick="increase('l_con')">+</button> |
|
<button onclick="decrease('l_con')">-</button> |
|
</div> |
|
</div> |
|
<div class="variables"> |
|
<div id="transfer_cut_par" class="variable"> |
|
Transfer Cutoff |
|
<div id="transfer_cut_div"> |
|
</div> |
|
<button onclick="increase('transfer_cut')">+</button> |
|
<button onclick="decrease('transfer_cut')">-</button> |
|
</div> |
|
<div id="transfer_par" class="variable"> |
|
Transfer Amount |
|
<div id="transfer_div"> |
|
</div> |
|
<button onclick="increase('transfer')">+</button> |
|
<button onclick="decrease('transfer')">-</button> |
|
</div> |
|
<div id="transfer_taper_par" class="variable"> |
|
Transfer Taper |
|
<div id="transfer_taper_div"> |
|
</div> |
|
<button onclick="increase('transfer_taper')">+</button> |
|
<button onclick="decrease('transfer_taper')">-</button> |
|
</div> |
|
</div> |
|
|
|
<div id="graphbox"> |
|
</div> |
|
<p> |
|
<button onclick="reversex()">Reverse X Axis Scale</button> |
|
</p> |
|
<div id="hours_change_par" class="result"> |
|
Change in hours |
|
<div id="hours_change"> |
|
</div> |
|
</div> |
|
</div> |
|
<script> |
|
|
|
width = 800; |
|
height = 600; |
|
|
|
var max_hours = 80 |
|
|
|
var wage = 100; |
|
$("#wage_div").html(wage) |
|
var tax = 0.5; |
|
$("#tax_div").html(tax) |
|
var c_pref = 0.5; |
|
$("#c_pref_div").html(c_pref) |
|
var l_pref = 0.5; |
|
$("#l_pref_div").html(l_pref) |
|
var c_con = 0.5; |
|
$("#c_con_div").html(c_con) |
|
var l_con = 0.5; |
|
$("#l_con_div").html(l_con) |
|
var transfer_cut = 1000; |
|
$("#transfer_cut_div").html(transfer_cut) |
|
var transfer = 500; |
|
$("#transfer_div").html(transfer) |
|
var transfer_taper = 0.5; |
|
$("#transfer_taper_div").html(transfer_taper) |
|
|
|
var hours = [{h:0, y:0, c: 0, l: 1, g: transfer}]; |
|
for (i = 0; i < max_hours + 1; i++) { |
|
h = i |
|
y = h*wage |
|
c = y*(1-tax) |
|
if (c > transfer_cut) { |
|
g = transfer-((c-transfer_cut)*transfer_taper) |
|
if (g < 0) { |
|
g = 0 |
|
} |
|
} else { |
|
g = transfer |
|
} |
|
c = c + g |
|
l = 1-i/max_hours |
|
u = Math.pow((l*4000)*l_pref,l_con)+Math.pow(c*c_pref,c_con) |
|
if (i == 0) { |
|
hours[i].h = h |
|
hours[i].y = y |
|
hours[i].c = c |
|
hours[i].l = l |
|
hours[i].u = u |
|
} else { |
|
hours.push({h: i, y: y, c: c, l: l, u: u, g: g}); |
|
} |
|
} |
|
|
|
max_consumption = d3.max(hours, function(d) { return d.c; }) * 2; |
|
max_utility = d3.max(hours, function(d) { return d.u; }); |
|
|
|
console.log(max_utility) |
|
|
|
for (i = 0; i < max_hours + 1; i++) { |
|
l = hours[i].l; |
|
cc = max_utility - Math.pow((l*4000)*l_pref,l_con) |
|
ci = Math.pow(cc, (1.0/c_con))/c_pref |
|
hours[i].ci = ci; |
|
} |
|
|
|
|
|
|
|
yticks = [0] |
|
x = 0; |
|
for (i = 1; x < max_consumption - 1; i++) { |
|
x = i * 500 |
|
yticks.push(x) |
|
} |
|
|
|
poshourscale = d3.scale.linear() |
|
.domain([0, max_hours]) |
|
.range([width*0.1, width*0.9]); |
|
|
|
neghourscale = d3.scale.linear() |
|
.domain([max_hours, 0]) |
|
.range([width*0.1, width*0.9]); |
|
|
|
hourscale = neghourscale; |
|
|
|
function reversex() { |
|
if (hourscale == neghourscale) { |
|
hourscale = poshourscale; |
|
draw() |
|
xaxis() |
|
} else { |
|
hourscale = neghourscale; |
|
draw() |
|
xaxis() |
|
} |
|
} |
|
|
|
incomescale = d3.scale.linear() |
|
.domain([0, max_consumption]) |
|
.range([height*0.9, height*0.15]); |
|
|
|
|
|
utilityscale = d3.scale.linear() |
|
.domain([0, Math.pow(max_consumption,0.5)]) |
|
.range([height*0.9, height*0.15]); |
|
|
|
|
|
console.log(hours) |
|
|
|
wage_increment = 10; |
|
tax_increment = 0.01; |
|
c_pref_increment = 0.1; |
|
l_pref_increment = 0.1; |
|
c_con_increment = 0.1; |
|
l_con_increment = 0.1; |
|
transfer_cut_increment = 50; |
|
transfer_increment = 50; |
|
transfer_taper_increment = 0.1; |
|
|
|
function increase(v, a) { |
|
if (v == "c_pref" && a == undefined) { |
|
decrease("l_pref", 1) |
|
} |
|
if (v == "l_pref" && a == undefined) { |
|
decrease("c_pref", 1) |
|
} |
|
this[v] += this[v+"_increment"] |
|
$("#"+v+"_div").html(this[v].toFixed(2)) |
|
calculate() |
|
} |
|
|
|
function decrease(v, a) { |
|
if (v == "c_pref" && a == undefined) { |
|
increase("l_pref", 1) |
|
} |
|
if (v == "l_pref" && a == undefined) { |
|
increase("c_pref", 1) |
|
} |
|
this[v] -= this[v+"_increment"] |
|
$("#"+v+"_div").html(this[v].toFixed(2)) |
|
calculate() |
|
} |
|
|
|
var graph = d3.select("#graphbox") |
|
.append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
graph.append("rect") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.style("fill", "white") |
|
.style("stroke", "black") |
|
.style("stroke-width", 2) |
|
|
|
graph.append("line") |
|
.attr("class", "axis") |
|
.attr("x1", width*0.1) |
|
.attr("x2", width* 0.9) |
|
.attr("y1", height*0.9) |
|
.attr("y2", height*0.9) |
|
.attr("stroke-width", 1) |
|
.attr("stroke", "black"); |
|
|
|
graph.append("line") |
|
.attr("class", "axis") |
|
.attr("x1", width*0.1) |
|
.attr("x2", width* 0.1) |
|
.attr("y1", height*0.9) |
|
.attr("y2", height*0.1) |
|
.attr("stroke-width", 1) |
|
.attr("stroke", "black"); |
|
|
|
xaxis() |
|
yaxis() |
|
|
|
|
|
draw() |
|
|
|
|
|
function calculate() { |
|
max_utility = d3.max(hours, function(d, i) { return d.u; }); |
|
for (i = 0; i < max_hours + 1; i++) { |
|
if (hours[i].u == max_utility) { |
|
hours_1 = hours[i].h |
|
} |
|
} |
|
|
|
|
|
for (i = 0; i < max_hours + 1; i++) { |
|
h = i |
|
y = h*wage |
|
c = y*(1-tax) |
|
if (c > transfer_cut) { |
|
g = transfer-((c-transfer_cut)*transfer_taper) |
|
if (g < 0) { |
|
g = 0 |
|
} |
|
} else { |
|
g = transfer |
|
} |
|
c = c + g |
|
l = 1-i/max_hours |
|
u = Math.pow((l*4000)*l_pref,l_con)+Math.pow(c*c_pref,c_con) |
|
hours[i].h = h |
|
hours[i].y = y |
|
hours[i].c = c |
|
hours[i].l = l |
|
hours[i].u = u |
|
} |
|
|
|
max_utility = d3.max(hours, function(d) { return d.u; }); |
|
|
|
for (i = 0; i < max_hours + 1; i++) { |
|
if (hours[i].u == max_utility) { |
|
hours_2 = hours[i].h |
|
} |
|
l = hours[i].l; |
|
cc = max_utility - Math.pow((l*4000)*l_pref,l_con) |
|
ci = Math.pow(cc, (1.0/c_con))/c_pref |
|
hours[i].ci = ci; |
|
} |
|
|
|
|
|
|
|
draw() |
|
|
|
delt_hours = hours_2 - hours_1 |
|
pcnt = (delt_hours/hours_1).toFixed(2) |
|
if (delt_hours > 0) { |
|
delt_hours = "<span style='color:green'>+"+delt_hours + " (+" + pcnt + "%)</span>" |
|
} else if (delt_hours < 0) { |
|
delt_hours = "<span style='color:red'>"+delt_hours + " (" + pcnt + "%)</span>" |
|
} else { |
|
delt_hours = delt_hours + " (" + pcnt + "%)" |
|
} |
|
$("#hours_change").html(delt_hours) |
|
|
|
} |
|
|
|
function draw() { |
|
graph.selectAll("circle").remove() |
|
graph.selectAll("path").remove() |
|
graph.selectAll("circle") |
|
.data(hours) |
|
.enter() |
|
.append("circle") |
|
.attr("class", "consumption") |
|
.attr("cx", function(d) { return hourscale(d.h)}) |
|
.attr("cy", function(d) { return incomescale(d.c)}) |
|
.attr("r", function(d) { |
|
if (d.u == max_utility) { |
|
return 3 |
|
} else { |
|
return 1 |
|
} |
|
}); |
|
|
|
graph.append("g").selectAll("circle") |
|
.data(hours) |
|
.enter() |
|
.append("circle") |
|
.attr("class", "utility") |
|
.attr("cx", function(d) { return hourscale(d.h)}) |
|
.attr("cy", function(d) { return incomescale(d.ci)}) |
|
.attr("r", 1) |
|
|
|
var indifference = d3.svg.line() |
|
.x(function(d) { |
|
return hourscale(d.h); |
|
}) |
|
.y(function(d) { |
|
return incomescale(d.ci); |
|
}) |
|
|
|
var income = d3.svg.line() |
|
.x(function(d) { |
|
return hourscale(d.h); |
|
}) |
|
.y(function(d) { |
|
return incomescale(d.c); |
|
}) |
|
|
|
|
|
graph.append("svg:path").attr("d", indifference(hours)); |
|
graph.append("svg:path").attr("d", income(hours)) |
|
.attr("class", "consumption"); |
|
} |
|
|
|
function xaxis() { |
|
d3.selectAll(".x_lab").remove() |
|
xax = graph.append("g").selectAll("text") |
|
.data(hours |
|
.filter(function(d) { |
|
if (d.h % 2 == 0) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
}) |
|
) |
|
.enter() |
|
.append("text") |
|
.attr("text-anchor", "middle") |
|
.attr("class", "x_lab") |
|
.style("fill", "black") |
|
.style("font-size", width/75 + "px") |
|
.attr("x", function(d) { return hourscale(d.h) }) |
|
.attr("y", height*0.95) |
|
.text(function(d) { return d.h}); |
|
} |
|
|
|
function yaxis() { |
|
yax = graph.append("g").selectAll("text") |
|
.data(yticks) |
|
.enter() |
|
.append("text") |
|
.attr("text-anchor", "middle") |
|
.style("fill", "black") |
|
.style("font-size", width/75 + "px") |
|
.attr("x", width*0.05) |
|
.attr("y", function(d) { return incomescale(d) }) |
|
.text(function(d) { return d}); |
|
} |
|
|
|
d3.select(self.frameElement).style("height", "950px"); |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |
Hey I'm an Econ student unfamiliar to D3.js ... would D3 be a good tool to draw things like this or this?