Preamble¶
from plotapi import PieFight
PieFight.set_license("your username", "your license key")
Introduction¶
Let’s take a look at changing how the event
information is displayed during a Pie Fight diagram.
Plotapi Pie Fight is a beautiful and feature rich take on the popular Pie Chart Race. As we can see, we have set our license details in the preamble with PieFight.set_license()
.
Dataset¶
Plotapi Pie Fight expects at minimum a list
of dictionary
items, these will define the value of our segments over time.
samples = [
"order": 0, "name": "Sankey", "value": 10,
"order": 0, "name": "Terminus", "value": 12,
"order": 0, "name": "Chord", "value": 8,
"order": 0, "name": "Bar Fight", "value": 9,
"order": 0, "name": "Pie Fight", "value": 12,
"order": 1, "name": "Sankey", "value": 18,
"order": 1, "name": "Terminus", "value": 24,
"order": 1, "name": "Chord", "value": 22,
"order": 1, "name": "Bar Fight", "value": 14,
"order": 1, "name": "Pie Fight", "value": 17,
"order": 2, "name": "Sankey", "value": 24,
"order": 2, "name": "Terminus", "value": 40,
"order": 2, "name": "Chord", "value": 32,
"order": 2, "name": "Bar Fight", "value": 19,
"order": 2, "name": "Pie Fight", "value": 42,
"order": 3, "name": "Sankey", "value": 32,
"order": 3, "name": "Terminus", "value": 62,
"order": 3, "name": "Chord", "value": 40,
"order": 3, "name": "Bar Fight", "value": 25,
"order": 3, "name": "Pie Fight", "value": 64,
"order": 4, "name": "Sankey", "value": 38,
"order": 4, "name": "Terminus", "value": 75,
"order": 4, "name": "Chord", "value": 55,
"order": 4, "name": "Bar Fight", "value": 45,
"order": 4, "name": "Pie Fight", "value": 120,
]
Next, we’ll start specifying event content to appear at certain points during our visualisation. Plotapi Pie Fight expects a list
of dictionary
items to configure each event.
events = [
"order": 0,
"event": '<p>Event information content can be <b>formatted with HTML</b>!</p><p>You can even include CSS - the <a href ="https://plotapi.com">power</a> of both are available.</p>'
,
"order": 1,
"event": '<b>Something Special</b><br><img src="https://datacrayon.com/datasets/pokemon_img/150.png"><p>Something special happened here, probably!</p>'
,
"order": 4,
"event": '<p>By specifying a <b>duration</b>, an event can be displayed for longer than the default value - much like this one!</p>',
]
Visualisation¶
Here we’re using .show()
which outputs to a Jupyter Notebook cell, however, we may want to output to an HTML file with .to_html()
instead. More on the different output methods later!
Be sure to interact with the visualisation to see what the default settings can do!
Default Event Mode¶
Without any additional configuration, the default event mode will pause the visualisation for the event duration
.
This duration is specified either per-event using the events
structure above, or globally using the event_duration
parameter as seen below.
PieFight(samples,
events=events,
event_duration=7500).show()
#plotapi-chart-2fd66c72 .title
stroke: #fcfcfc;
paint-order: stroke;
stroke-width: 3px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 0.4;
#plotapi-chart-2fd66c72 .current_order_text
font-size: 2em;
font-weight: bold;
fill-opacity: 0.4;
fill: #000;
font-size: 2em;
paint-order: stroke;
stroke: #fcfcfc;
stroke-width: 2px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 1;
font-weight: bold;
#plotapi-chart-2fd66c72
font-family: “Lato”, sans-serif;
text-align: center;
#plotapi-chart-2fd66c72 .event_detail_bg
fill: black;
opacity: 0.7;
#plotapi-chart-2fd66c72 .event_detail_text div,
#plotapi-chart-2fd66c72 .event_detail_text p,
#plotapi-chart-2fd66c72 .event_detail_text span
font-size: 16px;
#plotapi-chart-2fd66c72 text::selection,
#plotapi-chart-2fd66c72 tspan::selection,
#plotapi-chart-2fd66c72 div::selection,
#plotapi-chart-2fd66c72 img::selection,
#plotapi-chart-2fd66c72 p::selection
background: none;
#plotapi-chart-2fd66c72 div.box
height: var(–s);
width: var(–s);
border-radius: 50%;
color:#fff;
margin:auto auto;
font-size:16px;
line-height:1.25em;
text-align:center;
#plotapi-chart-2fd66c72 p
text-align:center !important;
#plotapi-chart-2fd66c72 .box i,
#plotapi-chart-2fd66c72 .box::before
content: ”;
float: left;
height:100%;
width: 50%;
shape-outside: radial-gradient(farthest-side at right, transparent calc(100% – var(–p)), #fff 0);
#plotapi-chart-2fd66c72 .box i
float: right;
shape-outside: radial-gradient(farthest-side at left, transparent calc(100% – var(–p)), #fff 0);
#plotapi-chart-2fd66c72 polyline
opacity: 0.3;
stroke-width: 2px;
fill: none;
shape-rendering: geometricPrecision;
#plotapi-chart-2fd66c72 polyline
opacity: 0.3;
stroke-width: 2px;
fill: none;
shape-rendering: geometricPrecision;
#plotapi-chart-2fd66c72 .label_text
stroke: #ffffff;
stroke-width: 1;
paint-order: stroke;
font-size:16px;
#plotapi-chart-2fd66c72 svg
-webkit-filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, .3));
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, .25));
#plotapi-chart-2fd66c72 img
max-width:78.33333333333333px;
display:inline;
(function() {
var jupyter_classic = !(typeof(IPython)===”undefined”);
var dependencies_paths =
‘d3’: ‘https://plotapi.com/static/js/d3.v7.min’,
‘pako’: ‘https://plotapi.com/static/js/pako.min’
if(jupyter_classic)
require.config(
paths: dependencies_paths
);
require([‘d3’, ‘pako’], function(d3, pako)
window.d3 = d3;
window.pako = pako;
plotapi_plot();
);
else{
var dependencies = Object.values(dependencies_paths);
function dependency_loader(dependencies_loaded){
var script = document.createElement(“script”);
script.type = “text/javascript”;
script.src = dependencies[dependencies_loaded] + “.js”;
script.onload = function ()
if(dependencies_loaded
update(elapsed);
);
function update(elapsed)
if (elapsed_time > 0)
d3.select(“#plotapi-chart-2fd66c72_svg .event_group”).transition().duration(250).style(“opacity”, 0);
elapsed_time = 0;
last_proc = elapsed;
else
if (sequence_index = 2000
let last_event_duration = 0;
if (elapsed – last_proc a.id);
contestants.sort((a, b) => b.target_value – a.target_value);
current_order = contestants.map((a) => a.id);
var delta = elapsed – last_proc;
draw(delta);
else if (sequence_index == sequence.length – 1)
last_event_duration = show_event();
contestants.sort((a, b) => b.target_value – a.target_value);
draw(2000);
d3.select(“#plotapi-chart-2fd66c72_svg .event_group”).transition().delay(last_event_duration).duration(250).style(“opacity”, 0);
sequence_index++;
if (sequence_index == sequence.length && !finished)
contestants.sort((a, b) => b.target_value – a.target_value);
update_minimap();
force_order(d3.easeCubic);
finished = true;
t.stop();
show_restart(last_event_duration);
}
function show_event()
var event_fired = false;
var element = events.find(item =>
return item.order === sequence_index
)
if(element == undefined)
return 0;
timer_stop(element);
d3.select(“#plotapi-chart-2fd66c72_svg .event_detail_text”).html(function (d)
return (
‘
element.event +
“
”
);
);
d3.select(“#plotapi-chart-2fd66c72_svg div.box”)
.style(“–s”,side+”px”)
.style(“–p”,”5px”)
d3.select(“#plotapi-chart-2fd66c72_svg .event_group”)
.interrupt()
.transition()
.duration(250)
.style(“opacity”, 1)
;
return element.duration ? element.duration : 7500
function update_current(current)
var event_fired = false;
var new_contestant = false;
var event_element = events.find(item =>
return item.order === current
)
if(event_element != undefined)
event_fired = true;
current_data = data.filter((d) => d.order == current);
for (var index = 0; index d.id == element.id);
if (contestant.length != 0)
if (!isNaN(element.value))
contestant[0].current_value = contestant[0].target_value;
contestant[0].target_value = element.value;
contestant[0].travel_scale = d3
.scaleLinear()
.domain([0, 2000])
.range([contestant[0].current_value, element.value]);
else
new_contestant = true;
var target_value = !isNaN(element.value) ? element.value : 0;
contestant_icon_image = icon(element.id);
contestant =
id: element.id,
color: color(element.id),
icon_href: contestant_icon_image,
current_value: target_value,
target_value: target_value,
travel_scale: d3
.scaleLinear()
.domain([0, 2000])
.range([target_value, target_value]),
;
contestants.push(contestant);
if(new_contestant)
var current_contestants;
current_contestants = contestants.filter((d) => d.current_value > 0);
arcs = pie(current_contestants);
pie_group.selectAll(“path.outer-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => darken_color(d.data.color, -0.5))
.attr(“class”, “outer-arc”)
.attr(“d”, outer_arc);
pie_group.selectAll(“path.inner-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => d.data.color)
.attr(“class”, “inner-arc”)
.attr(“d”, inner_arc);
pie_group.selectAll(“text”)
.data(arcs)
.join(“text”)
.attr(‘dy’, ‘.35em’)
.attr(“fill”, d => darken_color(d.data.color, 1))
.attr(“d”, outer_arc)
.attr(“class”, “label_text”)
.html(d => (midAngle(d)) > Math.PI ?
unique_names[d.data.id]
+ ” – ” +
” + format_value(d.data.current_value) + “”
:
” + format_value(d.data.current_value) + “”
+ ” – ” +
unique_names[d.data.id]
)
.attr(‘transform’, labelTransform)
.style(‘text-anchor’, function (d)
return (midAngle(d)) < Math.PI ? 'start' : 'end';
)
.style("opacity", function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) darken_color(d.data.color, 1))
.style(“display”, function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) d.icon_size)
.attr(“xlink:href”, d => d.data.icon_href)
.attr(“d”, outer_arc)
.attr(“transform”, d => ‘translate(‘+text_arc.centroid(d)+’)’)
.attr(“x”, d => -(d.icon_size / 2))
.attr(“y”, d => -(d.icon_size / 2))
if (sequence_index > 0)
update_minimap();
function force_order(easeFn)
icon_size = d3.max([(radius*0.3) – (icon_padding * 2), 0]);
function draw(delta)
for (var index = 0; index d.current_value);
if (new_max != current_max)
current_max = new_max;
}
var current_contestants;
current_contestants = contestants.filter((d) => d.current_value > 0);
arcs = pie(current_contestants);
pie_group.selectAll(“path.outer-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => darken_color(d.data.color, -0.5))
.attr(“class”, “outer-arc”)
.attr(“d”, outer_arc);
pie_group.selectAll(“path.inner-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => d.data.color)
.attr(“class”, “inner-arc”)
.attr(“d”, inner_arc);
pie_group.selectAll(“text”)
.data(arcs)
.join(“text”)
.attr(‘dy’, ‘.35em’)
.attr(“fill”, d => darken_color(d.data.color, 1))
.attr(“d”, outer_arc)
.attr(“class”,”label_text”)
.html(d => (midAngle(d)) > Math.PI ?
unique_names[d.data.id]
+ ” – ” +
” + format_value(d.data.current_value) + “”
:
” + format_value(d.data.current_value) + “”
+ ” – ” +
unique_names[d.data.id]
)
.attr(‘transform’, labelTransform)
.style(‘text-anchor’, function (d)
return (midAngle(d)) < Math.PI ? 'start' : 'end';
)
.style("opacity", function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) darken_color(d.data.color, 1))
.style(“display”, function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) d.icon_size)
.attr(“xlink:href”, d => d.data.icon_href)
.attr(“d”, outer_arc)
.attr(“transform”, d => ‘translate(‘+text_arc.centroid(d)+’)’)
.attr(“x”, d => -(d.icon_size / 2))
.attr(“y”, d => -(d.icon_size / 2))
function initialise()
contestants = [];
d3.select(“#plotapi-chart-2fd66c72_svg .minimap_group”)
.selectAll(“*”)
.remove();
last_proc = 0;
top_n = 5;
icon_padding =1;
text_padding = 5;
current_order = [];
last_order = [];
elapsed_time = 0;
sequence_index = 0;
current_max = null;
icon_size = d3.max([(radius*0.3) – (icon_padding * 2), 0]);
pie = d3.pie()
.padAngle(0.02)
.sort(function (a, b)
return unique_names[b.id]
.localeCompare(unique_names[a.id]);
)
.value(d => d.current_value)
update_current(sequence_index);
current_order_text.text(format_current_order(sequence[sequence_index]))
new_max = d3.max(contestants, (d) => d.current_value);
if (new_max != current_max)
current_max = new_max;
force_order(d3.easeCubic);
function darken_color(color, factor)
return d3.color(color).darker(factor)
function color(index)
node = nodes[index];
if (node.color)
return node.color;
var ratio = index / (5);
return d3.interpolateRainbow(ratio);
function icon(index)
node = nodes[index];
if (node.icon)
return node.icon;
return ” “;
function update_minimap()
var minimap_order = contestants.slice();
minimap_order.sort((a, b) => b.target_value – a.target_value);
minimap_order = minimap_order.slice(0,5);
current_sum = minimap_order
.map((item) => item.current_value)
.reduce((prev, next) => prev + next);
if (current_sum != 0)
var mm_bar_height = 47.0;
var mm_bar_drop_height = 141.0;
mm_y_scale = d3
.scaleLinear()
.domain([0, current_sum])
.range([0, mm_bar_height]);
mm_x_scale = d3
.scaleLinear()
.domain([0, sequence.length])
.range([0, 260.0]);
var mm_pos_x = mm_x_scale(sequence_index – 1);
var mm_width = mm_x_scale(1);
var mm_running_total = 0;
for (
var mm_index = 0; mm_index < minimap_order.length; mm_index++
)
const element = minimap_order[mm_index];
var mm_pos_y = mm_y_scale(mm_running_total);
var mm_height = mm_y_scale(element.current_value);
d3.select("#plotapi-chart-2fd66c72_svg .minimap_group")
.append("rect")
.attr("x", 520.0 + mm_pos_x)
.attr("y", 470 – mm_bar_drop_height + mm_pos_y)
.attr("width", mm_width)
.attr("height", mm_height)
.style("fill", color(element.id))
.style("opacity", 0)
.transition()
.duration(500)
.style("opacity", 1)
.attr("y", 470 – mm_bar_height + mm_pos_y);
mm_running_total += element.current_value;
function timer_stop(element)
elapsed_time = t._time;
t.stop();
d3.select("#plotapi-chart-2fd66c72_svg .proceed")
.attr("d", info_arc(
startAngle: 0,
endAngle: (0)
));
d3.select("#plotapi-chart-2fd66c72_svg .proceed")
.interrupt()
.transition()
.ease(d3.easeLinear)
.duration(element.duration ? element.duration : 7500)
.attrTween("d", arcTween)
t.restart(update, element.duration ? element.duration : 7500);
function restart()
d3.select("#plotapi-chart-2fd66c72_svg .event_group").interrupt().style("opacity", 0);
d3.select("#plotapi-chart-8886d59f_svg .proceed").interrupt().attr("d", info_arc(
startAngle: 0,
endAngle: (0)
))
finished = false;
restart_opacity_lock = false;
skip_first = true;
t.stop();
t.restart(update, 0);
initialise();
d3.select("#plotapi-chart-2fd66c72_restart")
.transition()
.ease(d3.easeQuad)
.duration(500)
.attr("width", 20)
.attr("height", 20)
.attr("x", 735)
.attr("y", -20)
.style("opacity", 0.6);
function show_restart(last_event_duration)
d3.select("#plotapi-chart-2fd66c72_restart")
.transition()
.delay(last_event_duration)
.style(
"opacity",
0.6
);
d3.select("#plotapi-chart-2fd66c72_restart")
.transition()
.ease(d3.easeQuad)
.delay(last_event_duration)
.duration(500)
.attr("width", 100)
.attr("height", 100)
.attr("x", 340.0)
.attr("y", 185.0)
.style("opacity", 0.6)
.on("end", function (d, i) restart_opacity_lock = true; );
function calculatePoints(d)
var pos = label_arc.centroid(d);
pos[0] = radius * 1.1 * (midAngle(d) < Math.PI ? 1 : -1);
return [outer_arc.centroid(d), label_arc.centroid(d), pos]
function midAngle(d)
return d.startAngle + (d.endAngle – d.startAngle) / 2;
function labelTransform(d)
var pos = label_arc.centroid(d);
pos[0] = radius * 1.12 * (midAngle(d) < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
function arcTween(d)
var interpolate = d3.interpolate(d.startAngle, d.endAngle);
return function (t)
maxAngArc = interpolate(t);
return info_arc(
startAngle: 0,
endAngle: (maxAngArc)
)
;
var event_detail_bg = d3.select("#plotapi-chart-2fd66c72_svg .event_group")
.append("circle")
.attr("cx", 390.0)
.attr("cy", 235.0)
.attr('r', radius * 0.55)
.style('fill', 'black')
.style('fill-opacity', 0.7)
.classed("event_detail_bg", true);
var event_detail_text = d3.select("#plotapi-chart-2fd66c72_svg .event_group")
.append("foreignObject")
.classed("event_detail_text", true)
.attr("x", (390.0 – (side / 2)))
.attr("y", (235.0 – (side / 2)))
.attr("width", side)
.attr("height", side)
.style("display", "block")
.style("color", "white")
.html(function (d)
return "";
);
d3.select("#plotapi-chart-2fd66c72_svg .event_group").style("opacity", 0);
var event_detail_autoplay = d3.select("#plotapi-chart-2fd66c72_svg .event_group")
.append("path")
.style("fill", "#333333")
.attr("d", info_arc(
startAngle: 0,
endAngle: (tau * 0)
))
.datum(
endAngle: tau,
startAngle: 0
)
.attr('transform', 'translate(' + 390.0 + ',' + 235.0 + ')')
.classed("proceed", true);
var current_order_text = d3
.select("#plotapi-chart-2fd66c72_svg .order_group")
.append("text")
.classed("current_order_text", true)
.attr("x", 775)
.attr("y", 465)
.text(format_current_order(sequence[sequence_index]))
.style("text-anchor", "end")
.style("dominant-baseline", "text-top");
initialise();
d3.select("#plotapi-chart-2fd66c72_svg .overlay_group")
.append("svg:a")
.attr("xlink:href", "https://plotapi.com")
.attr("target", "_blank")
.append("image")
.attr("xlink:href", "https://plotapi.com/gallery/icon/plotapi.svg")
.attr("width", 20)
.attr("height", 20)
.attr("x", 760)
.attr("y", -20)
.style("opacity", 0)
.attr("id", "plotapi-chart-2fd66c72_icon");
d3.select("#plotapi-chart-2fd66c72_icon")
.append("title")
.text("Produced with Plotapi");
d3.select("#plotapi-chart-2fd66c72_icon").on(
"mouseover",
function (d, i)
d3.select("#plotapi-chart-2fd66c72_icon").style("opacity", 1);
);
d3.select("#plotapi-chart-2fd66c72_icon").on(
"mouseout",
function (d, i)
d3.select("#plotapi-chart-2fd66c72_icon").style("opacity", 0.6);
);
d3.select("#plotapi-chart-2fd66c72_svg .overlay_group")
.append("svg:a")
.append("image")
.style("cursor", "pointer")
.attr("xlink:href", "https://plotapi.com/gallery/icon/restart.svg")
.attr("width", 20)
.attr("height", 20)
.attr("x", 735)
.attr("y", -20)
.style("opacity", 0)
.attr("id", "plotapi-chart-2fd66c72_restart");
d3.select("#plotapi-chart-2fd66c72_restart").on(
"click",
function (d, i)
restart();
);
d3.select("#plotapi-chart-2fd66c72_restart").on(
"mouseover",
function (d, i)
d3.select("#plotapi-chart-2fd66c72_restart").style("opacity", 1);
);
d3.select("#plotapi-chart-2fd66c72_restart").on(
"mouseout",
function (d, i)
d3.select("#plotapi-chart-2fd66c72_restart").style(
"opacity",
0.6
);
);
d3.select("#plotapi-chart-2fd66c72 svg").on(
"mouseenter",
function ()
d3.select("#plotapi-chart-2fd66c72_icon").style("opacity", 0.6);
if (!restart_opacity_lock)
d3.select("#plotapi-chart-2fd66c72_restart").style(
"opacity",
0.6
);
);
d3.select("#plotapi-chart-2fd66c72 svg").on(
"mouseleave",
function ()
d3.select("#plotapi-chart-2fd66c72_icon").style("opacity", 0);
if (!restart_opacity_lock)
d3.select("#plotapi-chart-2fd66c72_restart").style(
"opacity",
0
);
);
}());
Running Event Mode¶
By specifying event_pause=False
, we can keep the visualisation running during the duration of the event content.
In this mode, the event duration
is treated as a maximum duration, such that any subsequent events will interrupt the previous one.
PieFight(samples,
events=events,
event_pause=False).show()
#plotapi-chart-c30cd969 .title
stroke: #fcfcfc;
paint-order: stroke;
stroke-width: 3px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 0.4;
#plotapi-chart-c30cd969 .current_order_text
font-size: 2em;
font-weight: bold;
fill-opacity: 0.4;
fill: #000;
font-size: 2em;
paint-order: stroke;
stroke: #fcfcfc;
stroke-width: 2px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 1;
font-weight: bold;
#plotapi-chart-c30cd969
font-family: “Lato”, sans-serif;
text-align: center;
#plotapi-chart-c30cd969 .event_detail_bg
fill: black;
opacity: 0.7;
#plotapi-chart-c30cd969 .event_detail_text div,
#plotapi-chart-c30cd969 .event_detail_text p,
#plotapi-chart-c30cd969 .event_detail_text span
font-size: 16px;
#plotapi-chart-c30cd969 text::selection,
#plotapi-chart-c30cd969 tspan::selection,
#plotapi-chart-c30cd969 div::selection,
#plotapi-chart-c30cd969 img::selection,
#plotapi-chart-c30cd969 p::selection
background: none;
#plotapi-chart-c30cd969 div.box
height: var(–s);
width: var(–s);
border-radius: 50%;
color:#fff;
margin:auto auto;
font-size:16px;
line-height:1.25em;
text-align:center;
#plotapi-chart-c30cd969 p
text-align:center !important;
#plotapi-chart-c30cd969 .box i,
#plotapi-chart-c30cd969 .box::before
content: ”;
float: left;
height:100%;
width: 50%;
shape-outside: radial-gradient(farthest-side at right, transparent calc(100% – var(–p)), #fff 0);
#plotapi-chart-c30cd969 .box i
float: right;
shape-outside: radial-gradient(farthest-side at left, transparent calc(100% – var(–p)), #fff 0);
#plotapi-chart-c30cd969 polyline
opacity: 0.3;
stroke-width: 2px;
fill: none;
shape-rendering: geometricPrecision;
#plotapi-chart-c30cd969 polyline
opacity: 0.3;
stroke-width: 2px;
fill: none;
shape-rendering: geometricPrecision;
#plotapi-chart-c30cd969 .label_text
stroke: #ffffff;
stroke-width: 1;
paint-order: stroke;
font-size:16px;
#plotapi-chart-c30cd969 svg
-webkit-filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, .3));
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, .25));
#plotapi-chart-c30cd969 img
max-width:78.33333333333333px;
display:inline;
(function() {
var jupyter_classic = !(typeof(IPython)===”undefined”);
var dependencies_paths =
‘d3’: ‘https://plotapi.com/static/js/d3.v7.min’,
‘pako’: ‘https://plotapi.com/static/js/pako.min’
if(jupyter_classic)
require.config(
paths: dependencies_paths
);
require([‘d3’, ‘pako’], function(d3, pako)
window.d3 = d3;
window.pako = pako;
plotapi_plot();
);
else{
var dependencies = Object.values(dependencies_paths);
function dependency_loader(dependencies_loaded){
var script = document.createElement(“script”);
script.type = “text/javascript”;
script.src = dependencies[dependencies_loaded] + “.js”;
script.onload = function ()
if(dependencies_loaded
update(elapsed);
);
function update(elapsed)
if (elapsed_time > 0)
d3.select(“#plotapi-chart-c30cd969_svg .event_group”).transition().duration(250).style(“opacity”, 0);
elapsed_time = 0;
last_proc = elapsed;
else
let last_event_duration = 0;
if (elapsed – last_proc a.id);
contestants.sort((a, b) => b.target_value – a.target_value);
current_order = contestants.map((a) => a.id);
var delta = elapsed – last_proc;
draw(delta);
else if (sequence_index == sequence.length – 1)
last_event_duration = show_event();
contestants.sort((a, b) => b.target_value – a.target_value);
draw(2000);
sequence_index++;
if (sequence_index == sequence.length && !finished)
contestants.sort((a, b) => b.target_value – a.target_value);
update_minimap();
force_order(d3.easeCubic);
finished = true;
t.stop();
show_restart(last_event_duration);
}
function show_event()
var event_fired = false;
var element = events.find(item =>
return item.order === sequence_index
)
if(element == undefined)
return 0;
d3.select(“#plotapi-chart-c30cd969_svg .event_detail_text”).html(function (d)
return (
‘
element.event +
“
”
);
);
d3.select(“#plotapi-chart-c30cd969_svg div.box”)
.style(“–s”,side+”px”)
.style(“–p”,”5px”)
d3.select(“#plotapi-chart-c30cd969_svg .event_group”)
.interrupt()
.transition()
.duration(250)
.style(“opacity”, 1)
d3.select(“#plotapi-chart-c30cd969_svg .event_group”)
.transition()
.delay(element.duration ? element.duration : 5000)
.duration(250)
.style(“opacity”, 0)
;
return element.duration ? element.duration : 5000
function update_current(current)
var event_fired = false;
var new_contestant = false;
var event_element = events.find(item =>
return item.order === current
)
if(event_element != undefined)
event_fired = true;
current_data = data.filter((d) => d.order == current);
for (var index = 0; index d.id == element.id);
if (contestant.length != 0)
if (!isNaN(element.value))
contestant[0].current_value = contestant[0].target_value;
contestant[0].target_value = element.value;
contestant[0].travel_scale = d3
.scaleLinear()
.domain([0, 2000])
.range([contestant[0].current_value, element.value]);
else
new_contestant = true;
var target_value = !isNaN(element.value) ? element.value : 0;
contestant_icon_image = icon(element.id);
contestant =
id: element.id,
color: color(element.id),
icon_href: contestant_icon_image,
current_value: target_value,
target_value: target_value,
travel_scale: d3
.scaleLinear()
.domain([0, 2000])
.range([target_value, target_value]),
;
contestants.push(contestant);
if(new_contestant)
var current_contestants;
current_contestants = contestants.filter((d) => d.current_value > 0);
arcs = pie(current_contestants);
pie_group.selectAll(“path.outer-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => darken_color(d.data.color, -0.5))
.attr(“class”, “outer-arc”)
.attr(“d”, outer_arc);
pie_group.selectAll(“path.inner-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => d.data.color)
.attr(“class”, “inner-arc”)
.attr(“d”, inner_arc);
pie_group.selectAll(“text”)
.data(arcs)
.join(“text”)
.attr(‘dy’, ‘.35em’)
.attr(“fill”, d => darken_color(d.data.color, 1))
.attr(“d”, outer_arc)
.attr(“class”, “label_text”)
.html(d => (midAngle(d)) > Math.PI ?
unique_names[d.data.id]
+ ” – ” +
” + format_value(d.data.current_value) + “”
:
” + format_value(d.data.current_value) + “”
+ ” – ” +
unique_names[d.data.id]
)
.attr(‘transform’, labelTransform)
.style(‘text-anchor’, function (d)
return (midAngle(d)) < Math.PI ? 'start' : 'end';
)
.style("opacity", function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) darken_color(d.data.color, 1))
.style(“display”, function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) d.icon_size)
.attr(“xlink:href”, d => d.data.icon_href)
.attr(“d”, outer_arc)
.attr(“transform”, d => ‘translate(‘+text_arc.centroid(d)+’)’)
.attr(“x”, d => -(d.icon_size / 2))
.attr(“y”, d => -(d.icon_size / 2))
if (sequence_index > 0)
update_minimap();
function force_order(easeFn)
icon_size = d3.max([(radius*0.3) – (icon_padding * 2), 0]);
function draw(delta)
for (var index = 0; index d.current_value);
if (new_max != current_max)
current_max = new_max;
}
var current_contestants;
current_contestants = contestants.filter((d) => d.current_value > 0);
arcs = pie(current_contestants);
pie_group.selectAll(“path.outer-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => darken_color(d.data.color, -0.5))
.attr(“class”, “outer-arc”)
.attr(“d”, outer_arc);
pie_group.selectAll(“path.inner-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => d.data.color)
.attr(“class”, “inner-arc”)
.attr(“d”, inner_arc);
pie_group.selectAll(“text”)
.data(arcs)
.join(“text”)
.attr(‘dy’, ‘.35em’)
.attr(“fill”, d => darken_color(d.data.color, 1))
.attr(“d”, outer_arc)
.attr(“class”,”label_text”)
.html(d => (midAngle(d)) > Math.PI ?
unique_names[d.data.id]
+ ” – ” +
” + format_value(d.data.current_value) + “”
:
” + format_value(d.data.current_value) + “”
+ ” – ” +
unique_names[d.data.id]
)
.attr(‘transform’, labelTransform)
.style(‘text-anchor’, function (d)
return (midAngle(d)) < Math.PI ? 'start' : 'end';
)
.style("opacity", function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) darken_color(d.data.color, 1))
.style(“display”, function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) d.icon_size)
.attr(“xlink:href”, d => d.data.icon_href)
.attr(“d”, outer_arc)
.attr(“transform”, d => ‘translate(‘+text_arc.centroid(d)+’)’)
.attr(“x”, d => -(d.icon_size / 2))
.attr(“y”, d => -(d.icon_size / 2))
function initialise()
contestants = [];
d3.select(“#plotapi-chart-c30cd969_svg .minimap_group”)
.selectAll(“*”)
.remove();
last_proc = 0;
top_n = 5;
icon_padding =1;
text_padding = 5;
current_order = [];
last_order = [];
elapsed_time = 0;
sequence_index = 0;
current_max = null;
icon_size = d3.max([(radius*0.3) – (icon_padding * 2), 0]);
pie = d3.pie()
.padAngle(0.02)
.sort(function (a, b)
return unique_names[b.id]
.localeCompare(unique_names[a.id]);
)
.value(d => d.current_value)
update_current(sequence_index);
current_order_text.text(format_current_order(sequence[sequence_index]))
new_max = d3.max(contestants, (d) => d.current_value);
if (new_max != current_max)
current_max = new_max;
force_order(d3.easeCubic);
function darken_color(color, factor)
return d3.color(color).darker(factor)
function color(index)
node = nodes[index];
if (node.color)
return node.color;
var ratio = index / (5);
return d3.interpolateRainbow(ratio);
function icon(index)
node = nodes[index];
if (node.icon)
return node.icon;
return ” “;
function update_minimap()
var minimap_order = contestants.slice();
minimap_order.sort((a, b) => b.target_value – a.target_value);
minimap_order = minimap_order.slice(0,5);
current_sum = minimap_order
.map((item) => item.current_value)
.reduce((prev, next) => prev + next);
if (current_sum != 0)
var mm_bar_height = 47.0;
var mm_bar_drop_height = 141.0;
mm_y_scale = d3
.scaleLinear()
.domain([0, current_sum])
.range([0, mm_bar_height]);
mm_x_scale = d3
.scaleLinear()
.domain([0, sequence.length])
.range([0, 260.0]);
var mm_pos_x = mm_x_scale(sequence_index – 1);
var mm_width = mm_x_scale(1);
var mm_running_total = 0;
for (
var mm_index = 0; mm_index < minimap_order.length; mm_index++
)
const element = minimap_order[mm_index];
var mm_pos_y = mm_y_scale(mm_running_total);
var mm_height = mm_y_scale(element.current_value);
d3.select("#plotapi-chart-c30cd969_svg .minimap_group")
.append("rect")
.attr("x", 520.0 + mm_pos_x)
.attr("y", 470 – mm_bar_drop_height + mm_pos_y)
.attr("width", mm_width)
.attr("height", mm_height)
.style("fill", color(element.id))
.style("opacity", 0)
.transition()
.duration(500)
.style("opacity", 1)
.attr("y", 470 – mm_bar_height + mm_pos_y);
mm_running_total += element.current_value;
function restart()
d3.select("#plotapi-chart-c30cd969_svg .event_group").interrupt().style("opacity", 0);
d3.select("#plotapi-chart-8886d59f_svg .proceed").interrupt().attr("d", info_arc(
startAngle: 0,
endAngle: (0)
))
finished = false;
restart_opacity_lock = false;
skip_first = true;
t.stop();
t.restart(update, 0);
initialise();
d3.select("#plotapi-chart-c30cd969_restart")
.transition()
.ease(d3.easeQuad)
.duration(500)
.attr("width", 20)
.attr("height", 20)
.attr("x", 735)
.attr("y", -20)
.style("opacity", 0.6);
function show_restart(last_event_duration)
d3.select("#plotapi-chart-c30cd969_restart")
.transition()
.delay(last_event_duration)
.style(
"opacity",
0.6
);
d3.select("#plotapi-chart-c30cd969_restart")
.transition()
.ease(d3.easeQuad)
.delay(last_event_duration)
.duration(500)
.attr("width", 100)
.attr("height", 100)
.attr("x", 340.0)
.attr("y", 185.0)
.style("opacity", 0.6)
.on("end", function (d, i) restart_opacity_lock = true; );
function calculatePoints(d)
var pos = label_arc.centroid(d);
pos[0] = radius * 1.1 * (midAngle(d) < Math.PI ? 1 : -1);
return [outer_arc.centroid(d), label_arc.centroid(d), pos]
function midAngle(d)
return d.startAngle + (d.endAngle – d.startAngle) / 2;
function labelTransform(d)
var pos = label_arc.centroid(d);
pos[0] = radius * 1.12 * (midAngle(d) < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
function arcTween(d)
var interpolate = d3.interpolate(d.startAngle, d.endAngle);
return function (t)
maxAngArc = interpolate(t);
return info_arc(
startAngle: 0,
endAngle: (maxAngArc)
)
;
var event_detail_bg = d3.select("#plotapi-chart-c30cd969_svg .event_group")
.append("circle")
.attr("cx", 390.0)
.attr("cy", 235.0)
.attr('r', radius * 0.55)
.style('fill', 'black')
.style('fill-opacity', 0.7)
.classed("event_detail_bg", true);
var event_detail_text = d3.select("#plotapi-chart-c30cd969_svg .event_group")
.append("foreignObject")
.classed("event_detail_text", true)
.attr("x", (390.0 – (side / 2)))
.attr("y", (235.0 – (side / 2)))
.attr("width", side)
.attr("height", side)
.style("display", "block")
.style("color", "white")
.html(function (d)
return "";
);
d3.select("#plotapi-chart-c30cd969_svg .event_group").style("opacity", 0);
var event_detail_autoplay = d3.select("#plotapi-chart-c30cd969_svg .event_group")
.append("path")
.style("fill", "#333333")
.attr("d", info_arc(
startAngle: 0,
endAngle: (tau * 0)
))
.datum(
endAngle: tau,
startAngle: 0
)
.attr('transform', 'translate(' + 390.0 + ',' + 235.0 + ')')
.classed("proceed", true);
var current_order_text = d3
.select("#plotapi-chart-c30cd969_svg .order_group")
.append("text")
.classed("current_order_text", true)
.attr("x", 775)
.attr("y", 465)
.text(format_current_order(sequence[sequence_index]))
.style("text-anchor", "end")
.style("dominant-baseline", "text-top");
initialise();
d3.select("#plotapi-chart-c30cd969_svg .overlay_group")
.append("svg:a")
.attr("xlink:href", "https://plotapi.com")
.attr("target", "_blank")
.append("image")
.attr("xlink:href", "https://plotapi.com/gallery/icon/plotapi.svg")
.attr("width", 20)
.attr("height", 20)
.attr("x", 760)
.attr("y", -20)
.style("opacity", 0)
.attr("id", "plotapi-chart-c30cd969_icon");
d3.select("#plotapi-chart-c30cd969_icon")
.append("title")
.text("Produced with Plotapi");
d3.select("#plotapi-chart-c30cd969_icon").on(
"mouseover",
function (d, i)
d3.select("#plotapi-chart-c30cd969_icon").style("opacity", 1);
);
d3.select("#plotapi-chart-c30cd969_icon").on(
"mouseout",
function (d, i)
d3.select("#plotapi-chart-c30cd969_icon").style("opacity", 0.6);
);
d3.select("#plotapi-chart-c30cd969_svg .overlay_group")
.append("svg:a")
.append("image")
.style("cursor", "pointer")
.attr("xlink:href", "https://plotapi.com/gallery/icon/restart.svg")
.attr("width", 20)
.attr("height", 20)
.attr("x", 735)
.attr("y", -20)
.style("opacity", 0)
.attr("id", "plotapi-chart-c30cd969_restart");
d3.select("#plotapi-chart-c30cd969_restart").on(
"click",
function (d, i)
restart();
);
d3.select("#plotapi-chart-c30cd969_restart").on(
"mouseover",
function (d, i)
d3.select("#plotapi-chart-c30cd969_restart").style("opacity", 1);
);
d3.select("#plotapi-chart-c30cd969_restart").on(
"mouseout",
function (d, i)
d3.select("#plotapi-chart-c30cd969_restart").style(
"opacity",
0.6
);
);
d3.select("#plotapi-chart-c30cd969 svg").on(
"mouseenter",
function ()
d3.select("#plotapi-chart-c30cd969_icon").style("opacity", 0.6);
if (!restart_opacity_lock)
d3.select("#plotapi-chart-c30cd969_restart").style(
"opacity",
0.6
);
);
d3.select("#plotapi-chart-c30cd969 svg").on(
"mouseleave",
function ()
d3.select("#plotapi-chart-c30cd969_icon").style("opacity", 0);
if (!restart_opacity_lock)
d3.select("#plotapi-chart-c30cd969_restart").style(
"opacity",
0
);
);
}());
Interactive Event Mode¶
By specifying auto_proceed=False
, we can pause the visualisation and require user interaction for it to continue, i.e. clicking the continue button.
PieFight(samples,
events=events,
auto_proceed=False).show()
#plotapi-chart-fe7b2d1f .title
stroke: #fcfcfc;
paint-order: stroke;
stroke-width: 3px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 0.4;
#plotapi-chart-fe7b2d1f .current_order_text
font-size: 2em;
font-weight: bold;
fill-opacity: 0.4;
fill: #000;
font-size: 2em;
paint-order: stroke;
stroke: #fcfcfc;
stroke-width: 2px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 1;
font-weight: bold;
#plotapi-chart-fe7b2d1f
font-family: “Lato”, sans-serif;
text-align: center;
#plotapi-chart-fe7b2d1f .event_detail_bg
fill: black;
opacity: 0.7;
#plotapi-chart-fe7b2d1f .event_detail_text div,
#plotapi-chart-fe7b2d1f .event_detail_text p,
#plotapi-chart-fe7b2d1f .event_detail_text span
font-size: 16px;
#plotapi-chart-fe7b2d1f text::selection,
#plotapi-chart-fe7b2d1f tspan::selection,
#plotapi-chart-fe7b2d1f div::selection,
#plotapi-chart-fe7b2d1f img::selection,
#plotapi-chart-fe7b2d1f p::selection
background: none;
#plotapi-chart-fe7b2d1f div.box
height: var(–s);
width: var(–s);
border-radius: 50%;
color:#fff;
margin:auto auto;
font-size:16px;
line-height:1.25em;
text-align:center;
#plotapi-chart-fe7b2d1f p
text-align:center !important;
#plotapi-chart-fe7b2d1f .box i,
#plotapi-chart-fe7b2d1f .box::before
content: ”;
float: left;
height:100%;
width: 50%;
shape-outside: radial-gradient(farthest-side at right, transparent calc(100% – var(–p)), #fff 0);
#plotapi-chart-fe7b2d1f .box i
float: right;
shape-outside: radial-gradient(farthest-side at left, transparent calc(100% – var(–p)), #fff 0);
#plotapi-chart-fe7b2d1f polyline
opacity: 0.3;
stroke-width: 2px;
fill: none;
shape-rendering: geometricPrecision;
#plotapi-chart-fe7b2d1f polyline
opacity: 0.3;
stroke-width: 2px;
fill: none;
shape-rendering: geometricPrecision;
#plotapi-chart-fe7b2d1f .label_text
stroke: #ffffff;
stroke-width: 1;
paint-order: stroke;
font-size:16px;
#plotapi-chart-fe7b2d1f svg
-webkit-filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, .3));
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, .25));
#plotapi-chart-fe7b2d1f img
max-width:78.33333333333333px;
display:inline;
(function() {
var jupyter_classic = !(typeof(IPython)===”undefined”);
var dependencies_paths =
‘d3’: ‘https://plotapi.com/static/js/d3.v7.min’,
‘pako’: ‘https://plotapi.com/static/js/pako.min’
if(jupyter_classic)
require.config(
paths: dependencies_paths
);
require([‘d3’, ‘pako’], function(d3, pako)
window.d3 = d3;
window.pako = pako;
plotapi_plot();
);
else{
var dependencies = Object.values(dependencies_paths);
function dependency_loader(dependencies_loaded){
var script = document.createElement(“script”);
script.type = “text/javascript”;
script.src = dependencies[dependencies_loaded] + “.js”;
script.onload = function ()
if(dependencies_loaded
update(elapsed);
);
function update(elapsed)
if (elapsed_time > 0)
d3.select(“#plotapi-chart-fe7b2d1f_svg .event_group”).transition().duration(250).style(“opacity”, 0);
elapsed_time = 0;
last_proc = elapsed;
else
let last_event_duration = 0;
if (elapsed – last_proc a.id);
contestants.sort((a, b) => b.target_value – a.target_value);
current_order = contestants.map((a) => a.id);
var delta = elapsed – last_proc;
draw(delta);
else if (sequence_index == sequence.length – 1)
last_event_duration = show_event();
contestants.sort((a, b) => b.target_value – a.target_value);
draw(2000);
sequence_index++;
if (sequence_index == sequence.length && !finished)
contestants.sort((a, b) => b.target_value – a.target_value);
update_minimap();
force_order(d3.easeCubic);
finished = true;
t.stop();
}
function show_event()
var event_fired = false;
var element = events.find(item =>
return item.order === sequence_index
)
if(element == undefined)
return 0;
timer_stop(element);
d3.select(“#plotapi-chart-fe7b2d1f_svg .event_detail_text”).html(function (d)
return (
‘
element.event +
“
”
);
);
d3.select(“#plotapi-chart-fe7b2d1f_svg div.box”)
.style(“–s”,side+”px”)
.style(“–p”,”5px”)
d3.select(“#plotapi-chart-fe7b2d1f_svg .event_group”)
.interrupt()
.transition()
.duration(250)
.style(“opacity”, 1)
;
return element.duration ? element.duration : 5000
function update_current(current)
var event_fired = false;
var new_contestant = false;
var event_element = events.find(item =>
return item.order === current
)
if(event_element != undefined)
event_fired = true;
current_data = data.filter((d) => d.order == current);
for (var index = 0; index d.id == element.id);
if (contestant.length != 0)
if (!isNaN(element.value))
contestant[0].current_value = contestant[0].target_value;
contestant[0].target_value = element.value;
contestant[0].travel_scale = d3
.scaleLinear()
.domain([0, 2000])
.range([contestant[0].current_value, element.value]);
else
new_contestant = true;
var target_value = !isNaN(element.value) ? element.value : 0;
contestant_icon_image = icon(element.id);
contestant =
id: element.id,
color: color(element.id),
icon_href: contestant_icon_image,
current_value: target_value,
target_value: target_value,
travel_scale: d3
.scaleLinear()
.domain([0, 2000])
.range([target_value, target_value]),
;
contestants.push(contestant);
if(new_contestant)
var current_contestants;
current_contestants = contestants.filter((d) => d.current_value > 0);
arcs = pie(current_contestants);
pie_group.selectAll(“path.outer-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => darken_color(d.data.color, -0.5))
.attr(“class”, “outer-arc”)
.attr(“d”, outer_arc);
pie_group.selectAll(“path.inner-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => d.data.color)
.attr(“class”, “inner-arc”)
.attr(“d”, inner_arc);
pie_group.selectAll(“text”)
.data(arcs)
.join(“text”)
.attr(‘dy’, ‘.35em’)
.attr(“fill”, d => darken_color(d.data.color, 1))
.attr(“d”, outer_arc)
.attr(“class”, “label_text”)
.html(d => (midAngle(d)) > Math.PI ?
unique_names[d.data.id]
+ ” – ” +
” + format_value(d.data.current_value) + “”
:
” + format_value(d.data.current_value) + “”
+ ” – ” +
unique_names[d.data.id]
)
.attr(‘transform’, labelTransform)
.style(‘text-anchor’, function (d)
return (midAngle(d)) < Math.PI ? 'start' : 'end';
)
.style("opacity", function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) darken_color(d.data.color, 1))
.style(“display”, function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) d.icon_size)
.attr(“xlink:href”, d => d.data.icon_href)
.attr(“d”, outer_arc)
.attr(“transform”, d => ‘translate(‘+text_arc.centroid(d)+’)’)
.attr(“x”, d => -(d.icon_size / 2))
.attr(“y”, d => -(d.icon_size / 2))
if (sequence_index > 0)
update_minimap();
function force_order(easeFn)
icon_size = d3.max([(radius*0.3) – (icon_padding * 2), 0]);
function draw(delta)
for (var index = 0; index d.current_value);
if (new_max != current_max)
current_max = new_max;
}
var current_contestants;
current_contestants = contestants.filter((d) => d.current_value > 0);
arcs = pie(current_contestants);
pie_group.selectAll(“path.outer-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => darken_color(d.data.color, -0.5))
.attr(“class”, “outer-arc”)
.attr(“d”, outer_arc);
pie_group.selectAll(“path.inner-arc”)
.data(arcs)
.join(“path”)
.attr(“fill”, d => d.data.color)
.attr(“class”, “inner-arc”)
.attr(“d”, inner_arc);
pie_group.selectAll(“text”)
.data(arcs)
.join(“text”)
.attr(‘dy’, ‘.35em’)
.attr(“fill”, d => darken_color(d.data.color, 1))
.attr(“d”, outer_arc)
.attr(“class”,”label_text”)
.html(d => (midAngle(d)) > Math.PI ?
unique_names[d.data.id]
+ ” – ” +
” + format_value(d.data.current_value) + “”
:
” + format_value(d.data.current_value) + “”
+ ” – ” +
unique_names[d.data.id]
)
.attr(‘transform’, labelTransform)
.style(‘text-anchor’, function (d)
return (midAngle(d)) < Math.PI ? 'start' : 'end';
)
.style("opacity", function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) darken_color(d.data.color, 1))
.style(“display”, function (d)
if (((d.endAngle – d.startAngle) / (2 * Math.PI) * 100) d.icon_size)
.attr(“xlink:href”, d => d.data.icon_href)
.attr(“d”, outer_arc)
.attr(“transform”, d => ‘translate(‘+text_arc.centroid(d)+’)’)
.attr(“x”, d => -(d.icon_size / 2))
.attr(“y”, d => -(d.icon_size / 2))
function initialise()
contestants = [];
d3.select(“#plotapi-chart-fe7b2d1f_svg .minimap_group”)
.selectAll(“*”)
.remove();
last_proc = 0;
top_n = 5;
icon_padding =1;
text_padding = 5;
current_order = [];
last_order = [];
elapsed_time = 0;
sequence_index = 0;
current_max = null;
icon_size = d3.max([(radius*0.3) – (icon_padding * 2), 0]);
pie = d3.pie()
.padAngle(0.02)
.sort(function (a, b)
return unique_names[b.id]
.localeCompare(unique_names[a.id]);
)
.value(d => d.current_value)
update_current(sequence_index);
current_order_text.text(format_current_order(sequence[sequence_index]))
new_max = d3.max(contestants, (d) => d.current_value);
if (new_max != current_max)
current_max = new_max;
force_order(d3.easeCubic);
function darken_color(color, factor)
return d3.color(color).darker(factor)
function color(index)
node = nodes[index];
if (node.color)
return node.color;
var ratio = index / (5);
return d3.interpolateRainbow(ratio);
function icon(index)
node = nodes[index];
if (node.icon)
return node.icon;
return ” “;
function update_minimap()
var minimap_order = contestants.slice();
minimap_order.sort((a, b) => b.target_value – a.target_value);
minimap_order = minimap_order.slice(0,5);
current_sum = minimap_order
.map((item) => item.current_value)
.reduce((prev, next) => prev + next);
if (current_sum != 0)
var mm_bar_height = 47.0;
var mm_bar_drop_height = 141.0;
mm_y_scale = d3
.scaleLinear()
.domain([0, current_sum])
.range([0, mm_bar_height]);
mm_x_scale = d3
.scaleLinear()
.domain([0, sequence.length])
.range([0, 260.0]);
var mm_pos_x = mm_x_scale(sequence_index – 1);
var mm_width = mm_x_scale(1);
var mm_running_total = 0;
for (
var mm_index = 0; mm_index < minimap_order.length; mm_index++
)
const element = minimap_order[mm_index];
var mm_pos_y = mm_y_scale(mm_running_total);
var mm_height = mm_y_scale(element.current_value);
d3.select("#plotapi-chart-fe7b2d1f_svg .minimap_group")
.append("rect")
.attr("x", 520.0 + mm_pos_x)
.attr("y", 470 – mm_bar_drop_height + mm_pos_y)
.attr("width", mm_width)
.attr("height", mm_height)
.style("fill", color(element.id))
.style("opacity", 0)
.transition()
.duration(500)
.style("opacity", 1)
.attr("y", 470 – mm_bar_height + mm_pos_y);
mm_running_total += element.current_value;
function timer_stop(element)
elapsed_time = t._time;
t.stop();
d3.select("#plotapi-chart-fe7b2d1f_svg .proceed").attr("pointer-events", "all");
function restart()
d3.select("#plotapi-chart-fe7b2d1f_svg .event_group").interrupt().style("opacity", 0);
finished = false;
restart_opacity_lock = false;
skip_first = true;
t.stop();
t.restart(update, 0);
initialise();
d3.select("#plotapi-chart-fe7b2d1f_restart")
.transition()
.ease(d3.easeQuad)
.duration(500)
.attr("width", 20)
.attr("height", 20)
.attr("x", 735)
.attr("y", -20)
.style("opacity", 0.6);
function show_restart(last_event_duration)
d3.select("#plotapi-chart-fe7b2d1f_restart")
.transition()
.delay(last_event_duration)
.style(
"opacity",
0.6
);
d3.select("#plotapi-chart-fe7b2d1f_restart")
.transition()
.ease(d3.easeQuad)
.delay(last_event_duration)
.duration(500)
.attr("width", 100)
.attr("height", 100)
.attr("x", 340.0)
.attr("y", 185.0)
.style("opacity", 0.6)
.on("end", function (d, i) restart_opacity_lock = true; );
function calculatePoints(d)
var pos = label_arc.centroid(d);
pos[0] = radius * 1.1 * (midAngle(d) < Math.PI ? 1 : -1);
return [outer_arc.centroid(d), label_arc.centroid(d), pos]
function midAngle(d)
return d.startAngle + (d.endAngle – d.startAngle) / 2;
function labelTransform(d)
var pos = label_arc.centroid(d);
pos[0] = radius * 1.12 * (midAngle(d) < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
function arcTween(d)
var interpolate = d3.interpolate(d.startAngle, d.endAngle);
return function (t)
maxAngArc = interpolate(t);
return info_arc(
startAngle: 0,
endAngle: (maxAngArc)
)
;
var event_detail_bg = d3.select("#plotapi-chart-fe7b2d1f_svg .event_group")
.append("circle")
.attr("cx", 390.0)
.attr("cy", 235.0)
.attr('r', radius * 0.55)
.style('fill', 'black')
.style('fill-opacity', 0.7)
.classed("event_detail_bg", true);
var event_detail_text = d3.select("#plotapi-chart-fe7b2d1f_svg .event_group")
.append("foreignObject")
.classed("event_detail_text", true)
.attr("x", (390.0 – (side / 2)))
.attr("y", (235.0 – (side / 2)))
.attr("width", side)
.attr("height", side)
.style("display", "block")
.style("color", "white")
.html(function (d)
return "";
);
d3.select("#plotapi-chart-fe7b2d1f_svg .event_group").style("opacity", 0);
var event_proceed = d3.select("#plotapi-chart-fe7b2d1f_svg .event_group")
.append("image")
.style("cursor", "pointer")
.attr("xlink:href", "https://plotapi.com/gallery/icon/continue.svg")
.attr("x", 390.0 + (radius*.55))
.attr("y", 235.0)
.attr("width", 30)
.attr("height", 30)
.attr("rx", 5)
.attr("ry", 5)
.style("opacity", 1)
.classed("proceed", true);
d3.select("#plotapi-chart-fe7b2d1f_svg .proceed").on("click", function (d, i)
if(finished)
d3.select("#plotapi-chart-fe7b2d1f_svg .event_group").transition().duration(250).style("opacity", 0);
show_restart(0);
else
t.restart(update, 0);
d3.select("#plotapi-chart-fe7b2d1f_svg .proceed").attr("pointer-events", "none");
);
var current_order_text = d3
.select("#plotapi-chart-fe7b2d1f_svg .order_group")
.append("text")
.classed("current_order_text", true)
.attr("x", 775)
.attr("y", 465)
.text(format_current_order(sequence[sequence_index]))
.style("text-anchor", "end")
.style("dominant-baseline", "text-top");
initialise();
d3.select("#plotapi-chart-fe7b2d1f_svg .overlay_group")
.append("svg:a")
.attr("xlink:href", "https://plotapi.com")
.attr("target", "_blank")
.append("image")
.attr("xlink:href", "https://plotapi.com/gallery/icon/plotapi.svg")
.attr("width", 20)
.attr("height", 20)
.attr("x", 760)
.attr("y", -20)
.style("opacity", 0)
.attr("id", "plotapi-chart-fe7b2d1f_icon");
d3.select("#plotapi-chart-fe7b2d1f_icon")
.append("title")
.text("Produced with Plotapi");
d3.select("#plotapi-chart-fe7b2d1f_icon").on(
"mouseover",
function (d, i)
d3.select("#plotapi-chart-fe7b2d1f_icon").style("opacity", 1);
);
d3.select("#plotapi-chart-fe7b2d1f_icon").on(
"mouseout",
function (d, i)
d3.select("#plotapi-chart-fe7b2d1f_icon").style("opacity", 0.6);
);
d3.select("#plotapi-chart-fe7b2d1f_svg .overlay_group")
.append("svg:a")
.append("image")
.style("cursor", "pointer")
.attr("xlink:href", "https://plotapi.com/gallery/icon/restart.svg")
.attr("width", 20)
.attr("height", 20)
.attr("x", 735)
.attr("y", -20)
.style("opacity", 0)
.attr("id", "plotapi-chart-fe7b2d1f_restart");
d3.select("#plotapi-chart-fe7b2d1f_restart").on(
"click",
function (d, i)
restart();
);
d3.select("#plotapi-chart-fe7b2d1f_restart").on(
"mouseover",
function (d, i)
d3.select("#plotapi-chart-fe7b2d1f_restart").style("opacity", 1);
);
d3.select("#plotapi-chart-fe7b2d1f_restart").on(
"mouseout",
function (d, i)
d3.select("#plotapi-chart-fe7b2d1f_restart").style(
"opacity",
0.6
);
);
d3.select("#plotapi-chart-fe7b2d1f svg").on(
"mouseenter",
function ()
d3.select("#plotapi-chart-fe7b2d1f_icon").style("opacity", 0.6);
if (!restart_opacity_lock)
d3.select("#plotapi-chart-fe7b2d1f_restart").style(
"opacity",
0.6
);
);
d3.select("#plotapi-chart-fe7b2d1f svg").on(
"mouseleave",
function ()
d3.select("#plotapi-chart-fe7b2d1f_icon").style("opacity", 0);
if (!restart_opacity_lock)
d3.select("#plotapi-chart-fe7b2d1f_restart").style(
"opacity",
0
);
);
}());
You can do so much more than what’s presented in this example, and we’ll cover this in later sections. If you want to see the full list of growing features, check out the Plotapi Documentation.