Skip to content

Instantly share code, notes, and snippets.

@vkuchinov
Last active July 13, 2019 17:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkuchinov/a8900e6f1f14255fdbcc22cd0de10ec9 to your computer and use it in GitHub Desktop.
Save vkuchinov/a8900e6f1f14255fdbcc22cd0de10ec9 to your computer and use it in GitHub Desktop.
D3.JS Pie Stacks Mockup
var svg, chart, background, g, defs, div, fulldata, data, w = 800, h = 600, dist, offset = 64, vratio = 1.0, radius = 18, zAxis, zValues, ratio = 0.3814, stacksData = [], filter = false, filtered = [true, true, true], maxRation;;
var offset = 64;
var days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; //could be German or whatever
var states = ["Red Zone", "Yellow Zone", "Red Zone"];
var maxStack = Number.NEGATIVE_INFINITY;
var colors = ["#F0F0F0", "#FF652F", "#FFE400", "#14A76C"];
var scale = 20.0;
var zl = { x: -11.070 * scale, y: -11.993 * scale },
zm = { x: 0.0 * scale, y: -14.392 * scale },
zr = { x: 12.749 * scale, y: -12.466 * scale },
tp = { x: 0.0, y: 0.0 },
rp = { x: 12.749 * scale, y: 5.044 * scale },
bp = { x: 2.887 * scale, y: 14.904 * scale },
lp = { x: -11.070 * scale, y: 6.277 * scale };
d3.legend = function(){
function legend(selection_){
selection_.each(function(d_, i_) {
var g = d3.select(this).attr("transform", "translate(" + d_.x + "," + d_.y + ")")
var back = g.append("rect")
.attr("width", d_.w)
.attr("height", d_.h)
.attr("fill", "#DEDEDE")
.attr("opacity", 0.5);
var legendlabels = g.selectAll(".legendlabel")
.data(d_.states)
.enter()
.append("text")
.attr("class", function(d2_, j_){ return "legendText_" + j_; })
.attr("dx", 40)
.attr("dy", function(d2_, j_){ return 25 + j_ * 25; })
.text(function(d_){ return d_; })
var boxes = g.selectAll(".box")
.data(d_.states)
.enter()
.append("rect")
.attr("class", function(d2_, j_){ return "legendRect_" + j_; })
.attr("x", 20)
.attr("y", function(d2_, j_){ return 16 + j_ * 25; })
.attr("width", 12)
.attr("height", 12)
.attr("fill", function(d2_, j_){ return colors[j_ + 1]; });
var placeholder = g.selectAll(".box")
.data(d_.states)
.enter()
.append("rect")
.attr("x", 10)
.attr("y", function(d2_, j_){ return 11 + j_ * 25; })
.attr("width", 108)
.attr("height", 22)
.attr("fill", "transparent")
.on("click", function(d2_, j_){
filtered[j_] = !filtered[j_];
if(filtered[j_]){
d3.select(".legendText_" + j_).attr("fill", "#000000");
d3.select(".legendRect_" + j_).attr("fill", colors[j_ + 1]);
drawPieStacks();
}else{
if(atLeastOneLeft()){
d3.select(".legendText_" + j_).attr("fill", "#808080");
d3.select(".legendRect_" + j_).attr("fill", "#808080");
drawPieStacks();
}
}
})
});
}
return legend;
}
d3.pieStack = function(){
function pieStack(selection_){
selection_.each(function(d_, i_) {
var g = d3.select(this).attr("transform", "translate(" + (128 + (800 - 128)/ 10 * d_.y) + "," + ( 64 + (600 - 64) / days.length * d_.x) + ")")
var total = d_.total / maxRatio;
var outer = total, inner = total * 0.5;
this.arc = d3.arc().innerRadius(inner).outerRadius(outer);
this.pie = d3.pie().value(function(d2_) { return d2_.size; }).sort(null);
this.path = g.selectAll("path")
.attr("class", "chartElement")
.data(this.pie(d_.pie.children))
.enter()
.append("g")
.append("path")
.attr("d", this.arc)
.attr("fill", function(d2_) { return d2_.data.color; })
.on("mouseover", function(d2_){
var str = d2_.data.size + " tickets " + states[d2_.index] + " on " + d_.d + ", " + d_.a + " days old";
div.transition().duration(500).style("opacity", 0.9);
div.html(str).style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d2_){
div.transition().duration(500).style("opacity", 0);
});
g.append("text")
.attr("class", "chartElement")
.text(function(d2_){ if(outer > 15) { return d_.total; }})
.attr("text-anchor", "middle")
.attr("font-size", "8px")
.attr("dy", ".3em")
});
}
function filterStack(stacks_){
var out = [];
stacks_.forEach(function(d_, i_){ if(filtered[i_]) { out.push(d_); } });
return out;
}
return pieStack;
}
d3.json("mockup.json", function(error_, data_) {
if (error_) throw error_;
fulldata = data_;
inits();
});
function inits(){
console.log("%cWeekly Ticket Pie Grid β demo", "color: #494949; font-size: 18px; font-family: sans-serif;");
console.log("%cby Vladimir V KUCHINOV", "color: #494949; font-size: 12px; font-style: italic;font-family: sans-serif;");
svg = d3.select("body").append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 " + w + " " + h)
.classed("svg-content", true);
background = svg.append("rect").attr("width", w).attr("height", h).attr("fill", "transparent").on("click", function(){ filter = false, d3.selectAll(".cylinder").attr("opacity", 1.0); });
defs = svg.append("defs");
div = d3.select("#tooltip");
var weekdays = svg.selectAll(".weekday")
.data(days)
.enter()
.append("text")
.attr("dx", 48)
.attr("dy", function(d_, i_) { return 64 + (600 - 64) / days.length * i_; })
.attr("text-ancho", "end")
.text(function(d_) { return d_; });
var ages = svg.selectAll(".age")
.data(new Array(10).fill(0))
.enter()
.append("text")
.attr("dx", function(d_, i_) { return 128 + (800 - 128)/ 10 * i_; })
.attr("dy", 24)
.attr("text-ancho", "end")
.text(function(d_, i_) { return i_ + 1; })
drawPieStacks();
var legend = svg.selectAll(".legendBox")
.data([{states: states, x: w - 38 - 128, y: 38, w: 128, h: 92 }])
.enter().append("g")
.attr("class", "legendBox")
.call(d3.legend());
}
function atLeastOneLeft(){
var T = 0;
filtered.forEach(function(f_){ if(f_) { T++; } });
return T > 0 ? true : false;
}
function drawPieStacks(){
parseData(fulldata);
d3.selectAll("#chart").remove();
d3.selectAll(".chartElement").remove();
var g = svg.append("g").attr("id", "chart");
var stacks = g.selectAll(".stack")
.data(stacksData)
.enter()
.append("g")
.call(d3.pieStack())
}
function parseData(data_){
stacksData = [];
maxStack = 0;
var EPS = 5;
var days = Object.keys(data_);
days.forEach(function(d_, i_){
data_[d_].forEach(function(a_, j_){
var total = 0.0;
data = { name: d_ + ", " + a_.age + " days old", children: [] };
a_.stack.forEach(function(s_, k_){
if(filtered[k_]){ data.children.push({ group: k_, name : states[k_], color: colors[k_ + 1], size: s_}); total += s_; }
})
stacksData.push({ x: i_ , y: j_, d: d_, a: a_.age, stack: a_.stack, pie : data, total: total })
maxStack = Math.max(maxStack, total);
})
});
maxRatio = maxStack / 34.0;
}
function average1D(v0_, v1_){ return (Number(v0_) + Number(v1_)) / 2.0; }
function remapFloat(v_, min0_, max0_, min1_, max1_) {
return min1_ + (v_ - min0_) / (max0_ - min0_) * (max1_ - min1_);
}
function distance(x0_, y0_, x1_, y1_){
return Math.sqrt(Math.pow(x1_ - x0_, 2) + Math.pow(y1_ - y0_, 2));
}
function distance2D(v0_, v1_){
return Math.sqrt(Math.pow(v1_.x - v0_.x, 2) + Math.pow(v1_.y - v0_.y, 2));
}
function lerp2D(v0_, v1_, t_){
return { x: v0_.x * t_ + (1.0 - t_) * v1_.x, y: v0_.y * t_ + (1.0 - t_) * v1_.y };
}
function lerp1D(f0_, f1_, t_){ return f0_ * t_ + (1.0 - t_) * f1_; }
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Weekly Ticket Pie Grid φ demo</title>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
<style>
@import url("https://fonts.googleapis.com/css?family=Karla&display=swap");
body{ margin: 0; font-family: 'Karla', sans-serif; font-size: 12px; }
#d3placeholder {
margin: 0;
width: 100%;
height: 100%;
}
#tooltip {
position: absolute;
text-align: center;
padding: 8px;
font: 12px sans-serif;
background-color: #DEDEDE;
border: 0px;
border-radius: 8px;
pointer-events: none;
opacity: 0.0;
}
</style>
</head>
<body>
<div id="d3placeholder"></div>
<script src="app.js"></script>
<div id="tooltip"></div>
</body>
</html>
{
"Mon":[
{
"age": 1,
"stack":[
23,
26,
32
]
},
{
"age": 2,
"stack":[
15,
24,
44
]
},
{
"age": 3,
"stack":[
124,
109,
200
]
},
{
"age": 4,
"stack":[
151,
99,
151
]
},
{
"age": 5,
"stack":[
197,
141,
279
]
},
{
"age" :6,
"stack":[
105,
111,
240
]
},
{
"age": 7,
"stack":[
163,
101,
199
]
}
],
"Tue":[
{
"age": 1,
"stack":[
204,
131,
249
]
},
{
"age": 2,
"stack":[
22,
21,
32
]
},
{
"age": 3,
"stack":[
24,
24,
63
]
},
{
"age": 4,
"stack":[
154,
100,
202
]
},
{
"age": 5,
"stack":[
110,
100,
200
]
},
{
"age": 6,
"stack":[
142,
132,
237
]
},
{
"age": 7,
"stack":[
97,
68,
120
]
},
{
"age":8,
"stack":[
85,
55,
108
]
}
],
"Wed":[
{
"age": 1,
"stack":[
137,
109,
269
]
},
{
"age":2,
"stack":[
140,
132,
255
]
},
{
"age": 3,
"stack":[
22,
21,
40
]
},
{
"age": 4,
"stack":[
69,
24,
82
]
},
{
"age": 5,
"stack":[
107,
114,
250
]
},
{
"age": 6,
"stack":[
132,
98,
208
]
},
{
"age": 7,
"stack":[
121,
122,
223
]
},
{
"age": 8,
"stack":[
75,
60,
135
]
},
{
"age": 9,
"stack":[
75,
50,
90
]
}
],
"Thu":[
{
"age": 1,
"stack":[
125,
120,
250
]
},
{
"age": 2,
"stack":[
130,
105,
236
]
},
{
"age": 3,
"stack":[
143,
102,
241
]
},
{
"age": 4,
"stack":[
20,
20,
31
]
},
{
"age": 5,
"stack":[
28,
22,
40
]
},
{
"age": 6,
"stack":[
112,
65,
90
]
},
{
"age": 7,
"stack":[
40,
50,
120
]
},
{
"age": 8,
"stack":[
75,
75,
100
]
},
{
"age": 9,
"stack":[
60,
40,
70
]
},
{
"age": 10,
"stack":[
50,
35,
75
]
}
],
"Fri":[
{
"age": 1,
"stack":[
128,
160,
200
]
},
{
"age": 2,
"stack":[
100,
120,
225
]
},
{
"age": 3,
"stack":[
130,
100,
225
]
},
{
"age": 4,
"stack":[
175,
100,
200
]
},
{
"age": 5,
"stack":[
20,
20,
40
]
},
{
"age": 6,
"stack":[
25,
20,
25
]
},
{
"age": 7,
"stack":[
100,
75,
100
]
},
{
"age": 8,
"stack":[
80,
50,
120
]
},
{
"age": 9,
"stack":[
71,
78,
90
]
},
{
"age": 10,
"stack":[
55,
40,
65
]
}
],
"Sat":[
{
"age": 1,
"stack":[
130,
100,
190
]
},
{
"age": 2,
"stack":[
130,
115,
234
]
},
{
"age": 3,
"stack":[
125,
115,
240
]
},
{
"age": 4,
"stack":[
130,
100,
225
]
},
{
"age": 5,
"stack":[
175,
100,
250
]
},
{
"age": 6,
"stack":[
20,
15,
30
]
},
{
"age": 7,
"stack":[
20,
10,
20
]
},
{
"age": 8,
"stack":[
80,
50,
80
]
},
{
"age": 9,
"stack":[
76,
50,
111
]
},
{
"age": 10,
"stack":[
65,
64,
83
]
}
],
"Sun":[
{
"age": 1,
"stack":[
59,
30,
55
]
},
{
"age": 2,
"stack":[
135,
90,
166
]
},
{
"age": 3,
"stack":[
130,
115,
240
]
},
{
"age": 4,
"stack":[
120,
115,
230
]
},
{
"age": 5,
"stack":[
130,
112,
222
]
},
{
"age": 6,
"stack":[
175,
113,
220
]
},
{
"age": 7,
"stack":[
20,
20,
32
]
},
{
"age": 8,
"stack":[
10,
15,
20
]
},
{
"age": 9,
"stack":[
78,
50,
80
]
},
{
"age": 10,
"stack":[
80,
49,
100
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment