Skip to content

Instantly share code, notes, and snippets.

@saraquigley
Last active December 23, 2015 11:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save saraquigley/6631328 to your computer and use it in GitHub Desktop.
Save saraquigley/6631328 to your computer and use it in GitHub Desktop.
Strategy Spaces
// Chart design based on the recommendations of Stephen Few. Implementation
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
// http://projects.instantcognition.com/protovis/bulletchart/
// Forked by Sara Quigley to accommodate negative values and
// static axis that don't move when the values change
function bulletChart() {
var orient = "left", // TODO top & bottom
reverse = false,
duration = 100,
ranges = bulletRanges,
markers = bulletMarkers,
measures = bulletMeasures,
width = 400,
height = 40,
tickFormat = null
isUpdate = false;
// For each small multiple…
function bullet(g) {
g.each(function(d, i) {
var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
markerz = markers.call(this, d, i).slice(),
measurez = measures.call(this, d, i).slice().sort(d3.descending),
g = d3.select(this);
reverse = (d3.min([0, rangez[0], markerz[0], measurez[0]]) < 0 ? true : false);
// Compute the new x-scale.
var x1 = d3.scale.linear()
//.domain(d3.extent([0, rangez[0], markerz[0], measurez[0]])).nice() // SQ added this 7/18 for variable domain supporting negative values
.domain([d.min, d.max])
// .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) // SQ commented this 7/18
.range([0, width]);
// Retrieve the old x-scale, if this is an update.
var x0 = this.__chart__ || d3.scale.linear()
.domain([d.min, d.max])
//.domain(d3.extent([0, rangez[0], markerz[0], measurez[0]])).nice()
.range(x1.range());
if (this.childElementCount == 1) { isUpdate = true;}
// Stash the new scale.
this.__chart__ = x1;
// Derive width-scales from the x-scales.
var w0 = bulletWidth(x0),
w1 = bulletWidth(x1);
// Update the range rects.
var range = g.selectAll("rect.range")
.data(rangez);
range.enter().append("svg:rect")
.attr("class", function(d, i) { return "range s" + i; })
.attr("width", w0)
.attr("height", height)
.attr("x", d3.min([x0(d.ranges), x0(0)]))
.transition()
.duration(duration)
.attr("width", w1)
.attr("x", d3.min([x1(d.ranges), x1(0)]) );
range.transition()
.duration(duration)
.attr("x", d3.min([x1(d.ranges), x1(0)]))
.attr("width", w1)
.attr("height", height);
// Update the measure rects.
var measure = g.selectAll("rect.measure")
.data(measurez);
measure.enter().append("svg:rect")
.attr("class", function(d, i) { return "measure s" + i; })
.attr("width", w0)
.attr("height", height / 6)
.attr("x", d3.min([x0(d.measures), x0(0)]))
.attr("y", height * .45)
.transition()
.duration(duration)
.attr("width", w1)
.attr("x", d3.min([x1(d.measures), x1(0)]));
// if it's an update, glow; otherwise, don't
if (isUpdate) {
measure.transition()
.duration(1500)
.style("fill", "rgb(254, 217, 118)")
.transition()
.duration(1500)
.style("fill", "#969696")
.attr("width", w1)
.attr("height", height / 6)
.attr("x", d3.min([x1(d.measures), x1(0)]))
.attr("y", height * .45);
}
else {
measure.transition()
.delay(2000)
.duration(100)
.attr("width", w1)
.attr("height", height / 6)
.attr("x", d3.min([x1(d.measures), x1(0)]))
.attr("y", height * .45);
}
// Update the marker lines.
var marker = g.selectAll("line.marker")
.data(markerz);
marker.enter().append("svg:line")
.attr("class", function(d, i) { return "marker s" + i; })
.attr("x1", x0)
.attr("x2", x0)
.attr("y1", height / 8)
.attr("y2", height * 7 / 8)
.transition()
.duration(duration)
.attr("x1", x1)
.attr("x2", x1);
marker.transition()
.delay(2000)
.duration(2000)
.attr("x1", x1)
.attr("x2", x1)
.attr("y1", height / 8)
.attr("y2", height * 7 / 8);
// Compute the tick format.
var format = tickFormat || x1.tickFormat(8);
// Update the tick groups.
var tick = g.selectAll("g.tick")
.data(x1.ticks(8), function(d) {
return this.textContent || format(d);
});
// Initialize the ticks with the old scale, x0.
var tickEnter = tick.enter().append("svg:g")
.attr("class", "tick")
.attr("transform", bulletTranslate(x0))
.style("opacity", 1e-6);
tickEnter.append("svg:line")
.attr("y1", height)
.attr("y2", height * 7 / 6);
tickEnter.append("svg:text")
.attr("text-anchor", "middle")
.attr("dy", "1em")
.attr("y", height * 7 / 6)
.text(format);
// Transition the entering ticks to the new scale, x1.
tickEnter.transition()
.delay(1500)
.duration(1000)
.attr("transform", bulletTranslate(x1))
.style("opacity", 1);
// Transition the updating ticks to the new scale, x1.
var tickUpdate = tick.transition()
.delay(1500)
.duration(1000)
.attr("transform", bulletTranslate(x1))
.style("opacity", 1);
tickUpdate.select("line")
.attr("y1", height)
.attr("y2", height * 7 / 6);
tickUpdate.select("text")
.attr("y", height * 7 / 6);
// Transition the exiting ticks to the new scale, x1.
tick.exit().transition()
.delay(1000)
.duration(duration)
.attr("transform", bulletTranslate(x1))
.style("opacity", 1e-6)
.remove();
});
d3.timer.flush();
}
// left, right, top, bottom
bullet.orient = function(x) {
if (!arguments.length) return orient;
orient = x;
reverse = orient == "right" || orient == "bottom";
return bullet;
};
// ranges (bad, satisfactory, good)
bullet.ranges = function(x) {
if (!arguments.length) return ranges;
ranges = x;
return bullet;
};
// markers (previous, goal)
bullet.markers = function(x) {
if (!arguments.length) return markers;
markers = x;
return bullet;
};
// measures (actual, forecast)
bullet.measures = function(x) {
if (!arguments.length) return measures;
measures = x;
return bullet;
};
bullet.width = function(x) {
if (!arguments.length) return width;
width = x;
return bullet;
};
bullet.height = function(x) {
if (!arguments.length) return height;
height = x;
return bullet;
};
bullet.tickFormat = function(x) {
if (!arguments.length) return tickFormat;
tickFormat = x;
return bullet;
};
bullet.duration = function(x) {
if (!arguments.length) return duration;
duration = x;
return bullet;
};
return bullet;
};
function bulletRanges(d) {
return d.ranges;
}
function bulletMarkers(d) {
return d.markers;
}
function bulletMeasures(d) {
return d.measures;
}
function bulletTranslate(x) {
return function(d) {
return "translate(" + Math.abs(x(d)) + ",0)";
// return "translate(" + x(d) + ",0)";
};
}
function bulletWidth(x) {
var x0 = x(0);
return function(d) {
return Math.abs(x(d) - x0);
};
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="superformula.js"></script>
<script type="text/javascript" src="bulletQ.js"></script>
<script type="text/javascript" src="strategy_space_config.js"></script>
<title>Strategy Spaces</title>
<link type="text/css" rel="stylesheet" href="strategy_space.css"/>
</head>
<body>
<script type="text/javascript">// voronoi path for strategy space
var dispatch = d3.dispatch("load", "statechange");
var selectedSquare;
var defaultSquare = 5;
var voro_width = 220,
voro_height = 220,
bounds = d3.geom.polygon([[.5, .5], [.5, voro_height - .5], [voro_width - .5, voro_height - .5], [voro_width - .5, .5]]),
color = d3.scale.category10(),
p0 = [voro_width / 8, voro_width / 8],
p1 = [(voro_width / 8) * 3, voro_width / 8],
p2 = [(voro_width / 8) * 5, voro_width / 8],
p3 = [(voro_width / 8) * 7, voro_width / 8],
p4 = [voro_width / 8, (voro_width / 8) * 3],
p5 = [(voro_width / 8) * 3, (voro_width / 8) * 3],
p6 = [(voro_width / 8) * 5, (voro_width / 8) * 3],
p7 = [(voro_width / 8) * 7, (voro_width / 8) * 3],
p8 = [voro_width / 8, (voro_width / 8) * 5],
p9 = [(voro_width / 8) * 3, (voro_width / 8) * 5],
p10 = [(voro_width / 8) * 5, (voro_width / 8) * 5],
p11 = [(voro_width / 8) * 7, (voro_width / 8) * 5],
p12 = [voro_width / 8, (voro_width / 8) * 7],
p13 = [(voro_width / 8) * 3, (voro_width / 8) * 7],
p14 = [(voro_width / 8) * 5, (voro_width / 8) * 7],
p15 = [(voro_width / 8) * 7, (voro_width / 8) * 7];
var voronoi = d3.select("body").selectAll("svg")
.data([
[p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15] // squares
])
.enter().append("svg")
.attr("width", voro_width)
.attr("height", voro_height)
.attr("class", "voronoi");
var g = voronoi.selectAll("g")
.data(function(points) { return points; })
.enter().append("g")
.style("fill", "#ccc")
.style("fill-opacity", 0);
g.append("path")
.data(function(points) { return d3.geom.voronoi(points).map(clip); })
.attr("class", "voronoi")
.style("fill", "#e6e6e6")
.attr("id",function(d, i) { return i; })
.property("value", "")
.attr("d", function(polygon) { return "M" + polygon.join("L") + "Z"; });
g.append("circle")
.attr("r", 5)
.attr("class", "voronoi")
.attr("transform", function(point) { return "translate(" + point + ")"; });
d3.selectAll("path.voronoi").filter(function(d, i) { return i = d.id ? this : null; })
.style('fill', d3.rgb(31, 120, 180));
d3.selectAll("circle.voronoi").filter(function(d, i) { return i == defaultSquare ; })
.style("fill", "#f6b23d")
.style("fill-opacity", 1);
function clip(polygon) {
return bounds.clip(polygon);
}
g.selectAll("path")
.on("mouseover", function(d, i) {
d3.select(this)
.style('fill', d3.rgb(31, 120, 180));
voronoi.select('circle#point-'+i)
.style('fill', d3.rgb(31, 120, 180))
})
.on("mouseout", function(d, i) {
d3.select(this)
.style("fill", d3.rgb(230, 230, 230));
voronoi.select('circle#point-'+i)
.style('fill', 'black')
})
.on("click", function(d, i) {
//d3.selectAll("path.voronoi").property("value", "");
d3.selectAll("circle.voronoi")
.style("fill-opacity", 0);
d3.selectAll("circle.voronoi").filter(function(d, i) { return i == defaultSquare ; })
.style("fill", "#fff")
.style("fill-opacity", .85);
d3.select(this)
.style("fill", d3.rgb(31, 120, 180));
d3.select(this.nextSibling)
.transition() //use default duration
.style("fill-opacity", 1)
.style("fill", "#f6b23d"); // yellow
selectedSquare = this.id;
dispatch.statechange(this.id);
voronoi.select('circle#point-'+i)
.style('fill', 'black')
});
dispatch.on("statechange.slCaWorld", function(square) {
update(square);
});
function update(dilemmaPosition) {
d3.json("ss_lever.json", function(data) {
var outcomes = d3.selectAll(".lever")
.data(data[dilemmaPosition].bullets)
.transition()
.ease("cubic-in-out")
.delay(500)
.duration(4000)
.call(chart);
});
d3.json("ss_implication.json", function(data) {
var outcomes = d3.selectAll(".bulletGroup")
.data(data[dilemmaPosition].bullets)
.transition()
.ease("cubic-in-out")
.delay(2000)
.duration(3500)
.attr("transform", "translate(" + mO[3] + "," + mO[0] + ")")
.call(outcomeChart);
});
}
function updateLevers(d, slider) {
d.ranges = d.ranges;
d.markers = d.markers;
d.measures = d.measures.map(function(d) { return leverData[selectedSquare][d.index] ;})
return d;
}
</script> <!-- // voronoi -->
<script type="text/javascript">
var format = d3.format(".4n"),
scale = d3.scale.linear().domain([-10, 20, 1000]).range([0, 800, 1000]);
var svg = d3.select("body").append("svg")
.attr("width", 800)
.attr("height", 600)
.attr("class", "pathContainer");
var shape = d3.superformula()
.type("quigleyDiamond")
.size(75000)
.segments(3600);
var path = svg.append("path")
.attr("class", "diamond")
.attr("transform", "translate(300,300)")
.attr("d", shape);;
svg.append("radialGradient")
.attr("id", "radial-gradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 250)
.selectAll("stop")
.data([
{offset: "10%", color: "rgb(116, 169, 207)"}, // same as the action measures
{offset: "95%", color: "rgb(240, 240, 240)"}
])
.enter().append("stop")
.attr("offset", function(d) { return d.offset; })
.attr("stop-color", function(d) { return d.color; });
var labelData = [
{
"title": "Choice A",
"x": 300,
"y": 140,
"color": "rgb(37, 52, 148)"
},
{
"title": "Choice D",
"x": 520,
"y": 304,
"color": ""
},
{
"title": "Choice B",
"x": 300,
"y": 474,
"color": "rgb(37, 52, 148)"
},
{
"title": "Choice C",
"x": 85,
"y": 304,
"color": ""
}
];
// this needs to be refactored so that text elements are bound to labelData
svg.append("text")
.attr("class", "diamond-label")
.attr("transform", "translate(" + labelData[0].x +"," + labelData[0].y + ")")
.style("text-anchor", "middle")
.text(labelData[0].title);
svg.append("text")
.attr("class", "diamond-label")
.attr("transform", "translate(" + labelData[1].x +"," + labelData[1].y + ")")
.style("text-anchor", "middle")
.text(labelData[1].title);
svg.append("text")
.attr("class", "diamond-label")
.attr("transform", "translate(" + labelData[2].x +"," + labelData[2].y + ")")
.style("text-anchor", "middle")
.text(labelData[2].title);
svg.append("text")
.attr("class", "diamond-label")
.attr("transform", "translate(" + labelData[3].x +"," + labelData[3].y + ")")
.style("text-anchor", "middle")
.text(labelData[3].title);
// actions
var chart = bulletChart()
.width(w - m[1] - m[3])
.height(h - m[0] - m[2]);
d3.json("ss_lever.json", function(data) {
var vis = d3.select("#chart").selectAll("svg")
.data(data[defaultSquare].bullets)
.enter().append("svg")
.attr("class", "lever")
.attr("id", function(d) {return d.id ;})
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")")
.call(chart);
var title = vis.append("g")
.attr("text-anchor", "end")
.attr("transform", "translate(-6," + (h - m[0] - m[2]) / 2 + ")");
title.append("text")
.attr("class", "title")
.text(function(d) { return d.title; });
title.append("text")
.attr("class", "subtitle")
.attr("dy", "1.25em")
.text(function(d) { return d.subtitle; });
chart.duration(1000);
});
// implications
var outcomeChart = bulletChart()
.width(wO - mO[1] - mO[3])
.height(hO - mO[0] - mO[2]);
d3.json("ss_implication.json", function(data) {
// default is 10
var vis = d3.select("#implications").selectAll("svg")
.data(data[defaultSquare].bullets)
.enter().append("svg")
.attr("class", "implication")
.attr("id", function(d) {return d.id ;})
.attr("width", wO)
.attr("height", hO)
.append("g")
.attr("class", "bulletGroup")
.attr("transform", "translate(" + mO[3] + "," + mO[0] + ")")
.call(outcomeChart);
var title = vis.append("g")
.attr("text-anchor", "end")
.attr("transform", "translate(-6," + (hO - mO[0] - mO[2]) / 2 + ")");
title.append("text")
.attr("class", "title")
.text(function(d) { return d.title; });
title.append("text")
.attr("class", "subtitle")
.attr("dy", "1.25em")
.text(function(d) { return d.subtitle; });
outcomeChart.duration(1000);
});
</script>
<div></div>
<div id="header">Strategy Spaces:
<span id="sub-header">Crossing pairs of dilemma choices</span></div>
<div id="diamond"></div>
<div id="voronoi"></div>
<div id="chart"></div>
<div id="implications"></div>
</body>
</html>
[ { "ssPosition": 0,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
19
],
"markers": [
17,
19
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
80
],
"markers": [
85,
80
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
94
],
"markers": [
90,
94
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-146
],
"markers": [
-90,
-146
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 1,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
17
],
"markers": [
17,
17
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
80
],
"markers": [
85,
80
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
94
],
"markers": [
90,
94
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-90
],
"markers": [
-90,
-90
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 2,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
14
],
"markers": [
17,
14
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
75
],
"markers": [
85,
75
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
94
],
"markers": [
90,
94
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-60
],
"markers": [
-90,
-60
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 3,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
12
],
"markers": [
17,
12
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
70
],
"markers": [
85,
70
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
94
],
"markers": [
90,
94
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-10
],
"markers": [
-90,
-10
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 4,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
19
],
"markers": [
17,
19
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
85
],
"markers": [
85,
85
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
90
],
"markers": [
90,
90
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-146
],
"markers": [
-90,
-146
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 5,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
17
],
"markers": [
17,
17
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
85
],
"markers": [
85,
85
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
90
],
"markers": [
90,
90
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-90
],
"markers": [
-90,
-90
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 6,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
14
],
"markers": [
17,
14
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
80
],
"markers": [
85,
80
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
90
],
"markers": [
90,
90
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-60
],
"markers": [
-90,
-60
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 7,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
12
],
"markers": [
17,
12
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
75
],
"markers": [
85,
75
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
90
],
"markers": [
90,
90
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-10
],
"markers": [
-90,
-10
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 8,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
19
],
"markers": [
17,
19
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
90
],
"markers": [
85,
90
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
80
],
"markers": [
90,
80
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-146
],
"markers": [
-90,
-146
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 9,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
17
],
"markers": [
17,
17
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
90
],
"markers": [
85,
90
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
80
],
"markers": [
90,
80
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-90
],
"markers": [
-90,
-90
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 10,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
14
],
"markers": [
17,
14
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
85
],
"markers": [
85,
85
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
80
],
"markers": [
90,
80
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-60
],
"markers": [
-90,
-60
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 11,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
12
],
"markers": [
17,
12
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
80
],
"markers": [
85,
80
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
80
],
"markers": [
90,
80
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-10
],
"markers": [
-90,
-10
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 12,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
19
],
"markers": [
17,
19
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
95
],
"markers": [
85,
95
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
70
],
"markers": [
90,
70
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-146
],
"markers": [
-90,
-146
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 13,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
17
],
"markers": [
17,
17
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
95
],
"markers": [
70,
95
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
70
],
"markers": [
90,
70
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-90
],
"markers": [
-90,
-90
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 14,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
14
],
"markers": [
17,
14
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
70
],
"markers": [
85,
90
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
70
],
"markers": [
90,
70
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-60
],
"markers": [
-90,
-60
],
"min": -160,
"max": 50
}
] },
{ "ssPosition": 15,
"bullets": [
{
"title": "% Market Capitalization",
"subtitle": "% percentage ",
"id": "marketCap",
"ranges": [
16,
21,
21
],
"measures": [
12
],
"markers": [
17,
12
],
"min": 0,
"max": 20
},
{
"title": "Customer Experience",
"subtitle": "% positive overall experience",
"id":"stuExperience",
"ranges": [
60,
75,
100
],
"measures": [
85
],
"markers": [
85,
85
],
"min": 0,
"max": 100
},
{
"title": "Product Quality",
"subtitle": "Index, base value = 100",
"id":"productQuality",
"ranges": [
0,
50,
100
],
"measures": [
70
],
"markers": [
90,
70
],
"min": 0,
"max": 100
},
{
"title": "Revenue",
"subtitle": "$, in millions",
"id": "revenue",
"ranges": [
-100,
0,
60
],
"measures": [
-10
],
"markers": [
-90,
-10
],
"min": -160,
"max": 50
}
] }
]
[
{ "ssPosition": 0,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
12
],
"markers": [
20,
12
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
75
],
"markers": [
60,
75
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 1,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
20
],
"markers": [
20,
20
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
75
],
"markers": [
60,
75
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 2,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
25
],
"markers": [
20,
25
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
75
],
"markers": [
60,
75
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 3,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
35
],
"markers": [
20,
35
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
75
],
"markers": [
60,
75
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 4,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
12
],
"markers": [
20,
12
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
60
],
"markers": [
60,
60
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 5,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
20
],
"markers": [
20,
20
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
60
],
"markers": [
60,
60
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 6,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
25
],
"markers": [
20,
25
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
60
],
"markers": [
60,
60
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 7,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
35
],
"markers": [
20,
35
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
60
],
"markers": [
60,
60
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 8,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
12
],
"markers": [
20,
12
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
50
],
"markers": [
60,
50
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 9,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
20
],
"markers": [
20,
20
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
50
],
"markers": [
60,
50
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 10,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
25
],
"markers": [
20,
25
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
50
],
"markers": [
60,
50
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 11,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
35
],
"markers": [
20,
35
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
50
],
"markers": [
60,
50
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 12,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
12
],
"markers": [
20,
12
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
35
],
"markers": [
60,
35
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 13,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
20
],
"markers": [
20,
20
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
35
],
"markers": [
60,
35
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 14,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
25
],
"markers": [
20,
25
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
35
],
"markers": [
60,
35
],
"min": 0,
"max": 100
}
]
},
{ "ssPosition": 15,
"bullets": [
{
"title": "% Growth in International Sales",
"subtitle": "percentage %",
"id": "growthIntl",
"index" : 0,
"ranges": [
20,
28,
40
],
"measures": [
35
],
"markers": [
20,
35
],
"min": 0,
"max": 40
},
{
"title": "% Online Sales",
"subtitle": "% percentage",
"id": "onlineSales",
"ranges": [
100,
100,
100
],
"measures": [
35
],
"markers": [
60,
35
],
"min": 0,
"max": 100
}
]
}
]
#header {
position: absolute;
display: block;
top: 20px;
left: 120px;
font: 300 32px "Helvetica Neue";
color: #525252;
z-index: 0;
}
#sub-header {
top: 20px;
font: 300 32px "Gill Sans";
color: rgb(116, 169, 207);
}
path.diamond {
position: absolute;
top: 80px;
stroke-width: 0px;
stroke: rgb(224, 224, 224);
fill: url(#radial-gradient);
z-index: 0;
}
svg.pathContainer {
position: absolute;
top: 60px;
z-index: 0;
}
text.diamond-label {
position: absolute;
display: block;
fill: #525252;
font: 500 28px "Gill Sans";
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
svg.voronoi {
position: absolute;
top: 250px;
left: 198px;
z-index: 25;
}
svg.voronoi:nth-child(10n+1) {
margin-right: 0;
}
path.voronoi {
position: absolute;
top: 80px;
fill-opacity: .2;
stroke: #fff;
stroke-opacity: .75;
stroke-width: .5px;
z-index: 25;
}
circle.voronoi {
stroke: #fff;
}
#chart {
position: absolute;
left: 620px;
top: 200px;
}
#implications {
position: absolute;
left: 600px;
top: 440px;
}
.lever { font: 11px sans-serif;}
.lever .marker.s0 { stroke: rgb(116, 169, 207); stroke-width: 5px; }
.lever .marker.s1 { stroke: #f6b23d; stroke-width: 7px; }
.lever .axis line, .lever .axis path { stroke: #666; stroke-width: .5px; fill: none; }
.lever .range.s0 { fill-opacity: 1; fill: #bbb;}
.lever .range.s1 { fill-opacity: .75; fill: #ddd; }
.lever .range.s2 { fill-opacity: 1; fill: #eee;}
.lever .measure.s0 { fill: #969696; fill-opacity: .85;}
.lever .measure.s1 { fill: #525252; fill-opacity: 0;}
.lever .title { font-size: 20px; font-weight: 300; fill: #525252; font-family: Gill Sans;}
.lever .subtitle { fill: #999; font-size: 12px; }
.implication { font: 11px sans-serif;}
.implication .marker.s0 { stroke: rgb(116, 169, 207); stroke-width: 5px; }
.implication .marker.s1 { stroke: #f6b23d; stroke-width: 7px; }
.implication .axis line, .bullet .axis path { stroke: #666; stroke-width: .5px; }
.implication .range.s0 { fill-opacity: 0; fill: rgb(227, 26, 28);}
.implication .range.s1 { fill-opacity: 0; fill: rgb(255, 237, 111); }
.implication .range.s2 { fill-opacity: 0; fill: rgb(240, 240, 240);}
.implication .measure.s0 { fill: #969696; fill-opacity: .85;}
.implication .measure.s1 { fill: #525252; fill-opacity: 0;}
.implication .title { font-size: 20px; font-weight: 300; fill: #525252; font-family: Gill Sans;}
.implication .subtitle { fill: #999; font-size: 12px; }
.implication .label { font-size: 20px; font-weight: 400; fill: #525252; font-family: "Helvetica Neue";}
.implication .axis text {
font: 10px sans-serif;
}
.implication .axis path,
.implication .axis line {
fill: none;
stroke: rgb(221, 52, 151);
stroke-width: 5px;
shape-rendering: crispEdges;
}
// levers -------- actions
var w = 660,
h = 50,
m = [10, 50, 20, 300]; // top right bottom left
// implications
var wO = 680,
hO = 60,
mO = [10, 50, 20, 340]; // top right bottom left
// The awesome d3 superformula plugin : https://github.com/d3/d3-plugins/blob/master/superformula/superformula.js
(function() {
var _symbol = d3.svg.symbol(),
_line = d3.svg.line();
d3.superformula = function() {
var type = _symbol.type(),
size = _symbol.size(),
segments = size,
params = {};
function superformula(d, i) {
var n, p = _superformulaTypes[type.call(this, d, i)];
for (n in params) p[n] = params[n].call(this, d, i);
return _superformulaPath(p, segments.call(this, d, i), Math.sqrt(size.call(this, d, i)));
}
superformula.type = function(x) {
if (!arguments.length) return type;
type = d3.functor(x);
return superformula;
};
superformula.param = function(name, value) {
if (arguments.length < 2) return params[name];
params[name] = d3.functor(value);
return superformula;
};
// size of superformula in square pixels
superformula.size = function(x) {
if (!arguments.length) return size;
size = d3.functor(x);
return superformula;
};
// number of discrete line segments
superformula.segments = function(x) {
if (!arguments.length) return segments;
segments = d3.functor(x);
return superformula;
};
return superformula;
};
function _superformulaPath(params, n, diameter) {
var i = -1,
dt = 2 * Math.PI / n,
t,
r = 0,
x,
y,
points = [];
while (++i < n) {
t = params.m * (i * dt - Math.PI) / 4;
t = Math.pow(Math.abs(Math.pow(Math.abs(Math.cos(t) / params.a), params.n2)
+ Math.pow(Math.abs(Math.sin(t) / params.b), params.n3)), -1 / params.n1);
if (t > r) r = t;
points.push(t);
}
r = diameter * Math.SQRT1_2 / r;
i = -1; while (++i < n) {
x = (t = points[i] * r) * Math.cos(i * dt);
y = t * Math.sin(i * dt);
points[i] = [Math.abs(x) < 1e-6 ? 0 : x, Math.abs(y) < 1e-6 ? 0 : y];
}
return _line(points) + "Z";
}
var _superformulaTypes = {
asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1},
butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1},
circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1},
clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1},
cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1},
diamond: {m: 4, n1: .85, n2: 1, n3: 1, a: 1, b: 1},
quigleyDiamond: {m: 4, n1: 1.513, n2: 1, n3: 1, a: 1, b: 1},
quigleyCross: {m: 4.025, n1: .5750, n2: -0.6625, n3: -0.4375, a: -.7750, b: -2.5},
quigleySlider: {m: 4.000, n1: 15.16, n2: 17.15, n3: 11.86, a: 5.337, b: 1},
dilemmas6: {m: 6, n1: .85, n2: 1, n3: 1, a: 1, b: 1},
drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1},
ellipse: {m: 4, n1: 2, n2: 2, n3: 2, a: 9, b: 6},
gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1},
heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18},
heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1},
pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1},
rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1},
roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1},
square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1},
star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1},
triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1}
};
d3.superformulaTypes = d3.keys(_superformulaTypes);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment