|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
body { |
|
width: 1024px; |
|
margin-top: 0; |
|
margin: auto; |
|
font-family: "Lato", "PT Serif", serif; |
|
color: #222222; |
|
padding: 0; |
|
font-weight: 300; |
|
line-height: 33px; |
|
-webkit-font-smoothing: antialiased; |
|
} |
|
|
|
text { |
|
pointer-events: none; |
|
} |
|
|
|
.tile { |
|
stroke: #000; |
|
stroke-width: 1px; |
|
fill: #ecf0f1; |
|
-webkit-font-smoothing: antialiased; |
|
} |
|
|
|
.indenity rect, .ys rect, .impact rect { |
|
stroke: #000; |
|
stroke-width: 1px; |
|
stroke-dasharray: 4px, 4px; |
|
fill: #fff; |
|
-webkit-font-smoothing: antialiased; |
|
} |
|
|
|
.empty{ |
|
visibility: hidden; |
|
stroke: #000; |
|
stroke-width: 1px; |
|
stroke-dasharray: 4px, 4px; |
|
fill: #fff; |
|
-webkit-font-smoothing: antialiased; |
|
} |
|
|
|
.result{ |
|
stroke: #000; |
|
stroke-width: 1px; |
|
stroke-dasharray: 2px, 4px; |
|
fill: #fff; |
|
-webkit-font-smoothing: antialiased; |
|
} |
|
|
|
|
|
.label{ |
|
fill: #000; |
|
} |
|
|
|
|
|
.cols, .rows{ |
|
fill: #000; |
|
font-size: 18px; |
|
} |
|
|
|
.legend{ |
|
font-size: 20px; |
|
} |
|
|
|
.display{ |
|
font-size: 50px; |
|
} |
|
|
|
text { |
|
pointer-events: none; |
|
} |
|
|
|
.guide{ |
|
fill: none; |
|
stroke: red; |
|
stroke-width: 12px; |
|
opacity: 0.3; |
|
-webkit-font-smoothing: antialiased; |
|
} |
|
|
|
.fixed{ |
|
position: absolute; |
|
top: 550px; |
|
left: 830px; |
|
} |
|
|
|
button { |
|
background-color: #95a5a6; |
|
border: none; |
|
color: white; |
|
padding: 7.5px 18px; |
|
text-align: center; |
|
text-decoration: none; |
|
display: inline-block; |
|
font-size: 14px; |
|
border-radius: 4px; |
|
} |
|
button:hover{ |
|
background-color: #f39c12; |
|
} |
|
|
|
button:active{ |
|
background-color: #e67e22; |
|
} |
|
|
|
</style> |
|
<body> |
|
<button type="button" class='fixed' onclick="model()">New Problem</button> |
|
</body> |
|
<script src="//d3js.org/d3.v3.min.js"></script> |
|
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script> |
|
<script src="invert.js"></script> |
|
<script> |
|
var margin = { 'top': 50, 'right': 20, 'bottom': 50, 'left': 100 }, |
|
width = 960 - margin.left - margin.right, |
|
height = 593 - margin.top - margin.bottom, |
|
nitems = 5, |
|
sw = 600/(nitems), |
|
sh = 470/(nitems), |
|
translate_speed = 500; |
|
|
|
var model = function(){ |
|
var A_invert, rtiles, _R |
|
d3.selectAll('svg').remove() |
|
|
|
var svg = d3.select('body').append('svg') |
|
.attr('width', width + margin.left + margin.right) |
|
.attr('height', height + margin.top + margin.bottom) |
|
.append('g') |
|
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); |
|
|
|
buildI() |
|
|
|
var data = computeMatrix() |
|
|
|
var rs = ['Steel', 'Electric', 'Transport', 'Val. Added', 'Input'], |
|
cs = ['Steel', 'Electric', 'Transport', 'Demand', 'Output'] |
|
|
|
var cols = svg.selectAll('.cols') |
|
.data(cs).enter().append('text') |
|
.attr('class', function(d, i){ return i>2 ? 'extra cols' : 'need cols'}) |
|
.attr('transform', function(d,i){ |
|
return 'translate('+ ((sw)*i + sw/2 - 5) +','+ (-20) +')' |
|
}) |
|
.html(function(d,i){ return d}) |
|
.style('text-anchor', 'middle') |
|
|
|
var rows = svg.selectAll('.rs') |
|
.data(rs).enter().append('text') |
|
.attr('class', function(d, i){ return i>2 ? 'extra cols' : 'need cols'}) |
|
.attr('transform', function(d,i){ |
|
return 'translate('+ (-10) +','+ (i*(sh+10) + 10 + sh/2) +')' |
|
}) |
|
.style('text-anchor', 'end') |
|
.text(function(d){ return d}) |
|
|
|
tiles = svg.selectAll('.tiles') |
|
.data(data).enter().append('g') |
|
.selectAll('.tile') |
|
.data(function(d){ return d}).enter() |
|
.append('g').attr('class', function(d){ return d[4]+' g_'+d[0]+'_'+d[1]}) |
|
|
|
var rects = tiles.append('rect') |
|
.attr('transform', function(d){ return 'translate('+ ( d[1] * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('width', sw-10) |
|
.attr('height', sh) |
|
.attr('rx', 3) |
|
.attr('ry', 3) |
|
.attr('class', function(d, i){ return d[3]+' _'+d[0]+'_'+d[1]}) |
|
|
|
|
|
tiles.append('text') |
|
.attr('transform', function(d){ return 'translate('+ ( d[1] * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('x', sw/2) |
|
.attr('y', sh/2) |
|
.attr('dy', 7) |
|
.attr('dx', -5) |
|
.attr('class', function(d){ return 'r'+d[0]+'_'+d[1]}) |
|
.style('text-anchor', 'middle') |
|
.style('font-size', '24') |
|
.text(function(d){ return d[2]}); |
|
|
|
|
|
function buildI(){ |
|
var is = d3.range(3).map(function(d){ |
|
return d3.range(3).map(function(e){ return d==e ? [d,e,1] : [d,e,0] }) |
|
}) |
|
|
|
var itiles = svg.selectAll('.indenities') |
|
.data(is).enter().append('g').attr('class', 'indenity').style('visibility', 'hidden') |
|
.selectAll('.indenity') |
|
.data(function(d){ return d}).enter() |
|
|
|
itiles.append('rect') |
|
.attr('transform', function(d){ return 'translate('+ ( d[1] * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('width', sw-10) |
|
.attr('height', sh) |
|
.attr('rx', 3) |
|
.attr('ry', 3) |
|
|
|
|
|
itiles.append('text') |
|
.attr('transform', function(d){ return 'translate('+ ( d[1] * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('x', sw/2) |
|
.attr('y', sh/2) |
|
.attr('dy', 7) |
|
.attr('dx', -5) |
|
.style('text-anchor', 'middle') |
|
.style('font-size', '24') |
|
.text(function(d){ return d[2]}) |
|
|
|
var impact_mag = [100, 50, 25] |
|
var rs = d3.range(3).map(function(d){ |
|
return d3.range(3).map(function(e){ return d==e ? [d,e,impact_mag[d]] : [d,e,0] }) |
|
}) |
|
|
|
rtiles = svg.selectAll('.impacts') |
|
.data(rs).enter().append('g').attr('class', 'impact').style('visibility', 'hidden') |
|
.selectAll('.impact') |
|
.data(function(d){ return d}).enter() |
|
|
|
rtiles.append('rect') |
|
.attr('transform', function(d){ return 'translate('+ ( d[1] * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('width', sw-10) |
|
.attr('height', sh) |
|
.attr('rx', 3) |
|
.attr('ry', 3) |
|
|
|
|
|
rtiles.append('text') |
|
.attr('transform', function(d){ return 'translate('+ ( d[1] * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('x', sw/2) |
|
.attr('y', sh/2) |
|
.attr('dy', 7) |
|
.attr('dx', -5) |
|
.style('text-anchor', 'middle') |
|
.style('font-size', '24') |
|
.text(function(d){ return d[2]}) |
|
|
|
} |
|
|
|
function buildY(){ |
|
|
|
var ys = d3.range(3).map(function(d,i){ |
|
return d==0 ? [i,100] : [i,0] |
|
}) |
|
|
|
var itiles = svg.selectAll('.ys') |
|
.data(ys).enter().append('g').attr('class', 'ys') |
|
|
|
itiles.append('rect') |
|
.attr('transform', function(d){ return 'translate('+ ( 3.5 * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('width', sw-10) |
|
.attr('height', sh) |
|
.attr('rx', 3) |
|
.attr('ry', 3) |
|
|
|
|
|
itiles.append('text') |
|
.attr('transform', function(d){ return 'translate('+ ( 3.5 * (sw ) ) +','+ ( d[0] * (sh + 10) ) +')' }) |
|
.attr('x', sw/2) |
|
.attr('y', sh/2) |
|
.attr('dy', 7) |
|
.attr('dx', -5) |
|
.style('text-anchor', 'middle') |
|
.style('font-size', '24') |
|
.text(function(d){ return d[1]}) |
|
|
|
itiles.transition().duration(1500) |
|
.attr('transform', function(d){ |
|
return 'translate('+ ( (d[0]- 3.5) * (sw ) ) +','+ ( -d[0] * (sh + 10) ) +')' |
|
}) |
|
.transition().duration(2000) |
|
.attr('transform', function(d){ |
|
return 'translate('+ ( (d[0]- 3.5) * (sw ) ) +','+ ( (3.5-d[0]) * (sh + 10) ) +')' |
|
}) |
|
.transition().duration(1000) |
|
.style('opacity',0); |
|
|
|
|
|
d3.selectAll('.essential text').transition().duration(1000).delay(3500) |
|
.text(function(d){ |
|
var val = d3.select(this).text(), |
|
c = d3.select(this).data()[0][1]; |
|
|
|
return c==0 ? Math.round(val*100): '' |
|
}) |
|
d3.selectAll('.essential').transition().duration(1000).delay(3500) |
|
.style('opacity', function(d){ |
|
var c = d3.select(this).data()[0][1]; |
|
|
|
return c==0 ? 1: 0 |
|
}) |
|
.transition().duration(1000).delay(5000) |
|
.attr('transform', function(d){ |
|
return 'translate('+ ( 4.5*sh ) +','+ ( 0 ) +')' |
|
}) |
|
.each('end', function(d){ |
|
if(d[0]==d[1] && d[0]==2) buildR() |
|
}) |
|
} |
|
|
|
function buildR(){ |
|
d3.selectAll('.impact').transition().duration(1000) |
|
.style('visibility','visible') |
|
.transition().duration(1000).delay(3500) |
|
.attr('transform', function(d, i){ |
|
return 'translate(' + (650 - i*sw) + ',' + 0 + ')' |
|
}) |
|
|
|
d3.selectAll('.impact text').transition().duration(1000).delay(4000) |
|
.text(function(d){ |
|
return d[1]==d[0] ? Math.round(_R[d[0]]*d[2]/100)*100 : '' |
|
}) |
|
|
|
d3.selectAll('.impact rect') |
|
.transition().duration(1000).delay(3500) |
|
.style('fill', function(d){ |
|
return d[0]==d[1] ? '#f1c40f' : 'none' |
|
}) |
|
.style('stroke', 'none') |
|
|
|
d3.selectAll('.essential').transition().duration(1500) |
|
.attr('transform', function(d){ |
|
return 'translate('+ ( (d[0]) * (sw ) ) +','+ ( -d[0] * (sh + 10) ) +')' |
|
}) |
|
.transition().duration(2000) |
|
.attr('transform', function(d){ |
|
return 'translate('+ ( (d[0]) * (sw ) ) +','+ ( (3.5-d[0]) * (sh + 10) ) +')' |
|
}) |
|
.transition().duration(1000) |
|
.style('opacity',0.5) |
|
.attr('transform','translate(0,0)') |
|
|
|
|
|
d3.selectAll('.essential text').transition().duration(1500).delay(4000) |
|
.text(function(d){ return d[2]}); |
|
|
|
} |
|
|
|
function computeMatrix(){ |
|
var data = d3.range(nitems).map(function(d){ |
|
return d3.range(nitems).map(function(e){ return [d,e, +Math.ceil(Math.random()*8)*100, 'tile', 'essential']}) |
|
}) |
|
for (var i = 0; i < nitems; i++) { |
|
data[i][4][2] = data[i][0][2] + data[i][1][2] + data[i][2][2] + data[i][3][2] |
|
data[4][i][2] = data[i][4][2] |
|
data[3][i][2] = data[4][i][2] - (data[0][i][2] + data[1][i][2] + data[2][i][2]) |
|
if (data[3][i][2] < 0) return computeMatrix() |
|
if (i > 2) { |
|
data[0][i][3] = 'result' |
|
data[1][i][3] = 'result' |
|
data[2][i][3] = 'result' |
|
data[i][0][3] = 'result' |
|
data[i][1][3] = 'result' |
|
data[i][2][3] = 'result' |
|
|
|
data[0][i][4] = 'extra' |
|
data[1][i][4] = 'extra' |
|
data[2][i][4] = 'extra' |
|
data[i][0][4] = 'extra' |
|
data[i][1][4] = 'extra' |
|
data[i][2][4] = 'extra' |
|
} |
|
} |
|
data[3][3] = [3,3,'', 'empty', 'extra'] |
|
data[3][4] = [3,4,'', 'empty', 'extra'] |
|
data[4][3] = [4,3,'', 'empty', 'extra'] |
|
data[4][4] = [4,4,'', 'empty', 'extra'] |
|
return data |
|
} |
|
|
|
function normalize(callback){ |
|
var A = [[0,0,0], [0,0,0], [0,0,0]]; |
|
|
|
for (var i = 0; i < 3; i++) { |
|
d3.select('.g_4_'+i).transition().duration(1800) |
|
.style('opacity', '0.98') |
|
.attr('transform', 'translate(' + (0) + ',' + (-4*(sh+10)) + ')') |
|
.transition().duration(1000).delay(2000) |
|
.style('opacity', '1') |
|
.attr('transform', 'translate(' + (0) + ',' + (0) + ')'); |
|
|
|
var norm = d3.select('._4_'+i).data()[0][2], |
|
c1 = d3.select('._'+i+'_0').data()[0][2], |
|
c2 = d3.select('._'+i+'_1').data()[0][2], |
|
c3 = d3.select('._'+i+'_2').data()[0][2]; |
|
|
|
d3.select('.r'+i+'_0').transition().duration(1000).delay(3000).text(Math.round(c1/norm*100)/100) |
|
d3.select('.r'+i+'_1').transition().duration(1000).delay(3000).text(Math.round(c2/norm*100)/100) |
|
d3.select('.r'+i+'_2').transition().duration(1000).delay(3000).text(Math.round(c3/norm*100)/100) |
|
A[i][0] = i==0 ? 1 - (c1/norm) : -c1/norm |
|
A[i][1] = i==1 ? 1 - (c2/norm) : -c2/norm |
|
A[i][2] = i==2 ? 1 - (c3/norm) : -c3/norm |
|
|
|
} |
|
var A_ = matrix_invert(A), |
|
fin_R = [A_[0][0]*100, A_[1][0]*100, A_[1][0]*100] |
|
|
|
callback(A_, fin_R) |
|
} |
|
|
|
|
|
function run(){ |
|
normalize(function(A_, fin_R){ |
|
|
|
A_invert = A_, |
|
_R = fin_R |
|
|
|
d3.selectAll('.extra').transition().duration(1000).delay(3500) |
|
.style('opacity',0) |
|
.transition().duration(1000).delay(25500) |
|
.style('opacity',0.5) |
|
|
|
d3.selectAll('.need').transition().duration(1000).delay(3500) |
|
.style('opacity',0) |
|
.transition().duration(1000).delay(14500) |
|
.style('opacity',1) |
|
.transition().duration(1000).delay(33000) |
|
.style('opacity',0.5) |
|
|
|
d3.selectAll('.indenity').transition().duration(1000).delay(4000) |
|
.style('visibility','visible') |
|
.transition().duration(1000).delay(6000) |
|
.attr('transform', 'translate(' + 400 + ',' + 0 + ')') |
|
.transition().duration(1000).delay(8000) |
|
.style('opacity',0) |
|
|
|
// Start next chain of events |
|
d3.selectAll('.essential').transition().duration(1000).delay(3500) |
|
.each("end", function(){ |
|
d3.select(this).transition().duration(1000) |
|
.attr('transform', 'translate(' + 400 + ',' + 0 + ')') |
|
.each("end",updateI) |
|
}) |
|
|
|
svg.append('text') |
|
.attr('x', width*0.70) |
|
.attr('y', height*0.85) |
|
.text('Normalize by Total Input') |
|
.style('font-size', '32') |
|
.style('text-anchor', 'middle') |
|
.style('opacity',1) |
|
.transition().duration(1000).delay(4000) |
|
.text('Subtract from Identity') |
|
.transition().duration(1000).delay(9000) |
|
.text('Invert Matrix') |
|
.transition().duration(1000).delay(12000) |
|
.style('opacity',0) |
|
.transition().duration(1000).delay(13500) |
|
.style('opacity',1) |
|
.text('Multiply by Added Demand') |
|
.transition().duration(1000).delay(20500) |
|
.text('Multiply Activity Vector...') |
|
.transition().duration(1000).delay(22000) |
|
.text('... by the Impact Matrix') |
|
.transition().duration(1000).delay(24000) |
|
.text('Ans: Impacts by Sector') |
|
|
|
}) |
|
} |
|
|
|
function updateI(){ |
|
var val = d3.select(this).text(), |
|
r = d3.select(this).data()[0][0], |
|
c = d3.select(this).data()[0][1] |
|
|
|
d3.select(this).select('text').transition().duration(1000).delay(2500) |
|
.text(r==c ? Math.round((1-val)*100)/100: Math.round((-val)*100)/100) |
|
.transition().duration(1000).delay(5000) |
|
.text(Math.round((A_invert[r][c])*100)/100) ; |
|
|
|
d3.select(this).transition().duration(2000).delay(7000) |
|
.attr('transform', 'translate(' + 0 + ',' + 0 + ')') |
|
.each('end', function(d){ |
|
if(d[0]==d[1] && d[0]==2) buildY() |
|
}) |
|
|
|
} |
|
|
|
run() |
|
} |
|
|
|
model() |
|
|
|
d3.select(self.frameElement).style("height", 620 + "px"); |
|
</script> |