• Jump To … +
    butterfly_example.ts dark_example.ts flamechart_example.ts icicle_sunburst_example.ts legend_example.ts pie_example.ts plot_area_example.ts plot_zoom_example.ts region_example.ts sankey_example.ts scatterplot_example.ts stack_example.ts swimlanes_example.ts sync_example.ts table_searchable_example.ts table_selectable_example.ts table_sortable_example.ts timeline_example.ts worldbank_example.ts
  • ¶

    C3 Butterfly Dark Family Tree

    Demonstrates a Butterfly Caller/Callee flow chart for a family tree based on time travel for the Netflix series “Dark”.

  • ¶

    Family Datasets

  • ¶

    People relations contain a parent and a child

    interface Relation {
        parent: string;
        child: string;
        season: number;
        type?: 'adopted';
    }
    
    var world = 'jonas';
    var season = 1;
  • ¶

    Example data sets

    var family_datasets = {
        jonas: {
            relations: [
                {parent: 'Elisabeth Doppler', child: 'Charlotte Doppler', season: 2},
                {parent: 'Noah', child: 'Charlotte Doppler', season: 2},
                {parent: 'Charlotte Doppler', child: 'Elisabeth Doppler', season: 1},
                {parent: 'Peter Doppler', child: 'Elisabeth Doppler', season: 1},
                {parent: 'Charlotte Doppler', child: 'Franziska Doppler', season: 1},
                {parent: 'Peter Doppler', child: 'Franziska Doppler', season: 1},
                {parent: 'Helge Doppler', child: 'Peter Doppler', season: 1},
                {parent: 'Ulla Schmidt', child: 'Peter Doppler', season: 3},
                {parent: 'Greta Doppler', child: 'Helge Doppler', season: 1},
                {parent: 'Bernd Doppler', child: 'Helge Doppler', season: 1},
                {parent: 'Katharina Nielsen', child: 'Magnus Nielsen', season: 1},
                {parent: 'Ulrich Nielsen', child: 'Magnus Nielsen', season: 1},
                {parent: 'Katharina Nielsen', child: 'Martha Nielsen', season: 1},
                {parent: 'Ulrich Nielsen', child: 'Martha Nielsen', season: 1},
                {
                  parent: 'Katharina Nielsen',
                  child: 'Mikkel Nielsen / Michael Kahnwald', season: 1
                },
                {
                  parent: 'Ulrich Nielsen',
                  child: 'Mikkel Nielsen / Michael Kahnwald', season: 1
                },
                {
                  parent: 'Mikkel Nielsen / Michael Kahnwald',
                  child: 'Jonas Kahnwald', season: 1
                },
                {parent: 'Hannah Kahnwald', child: 'Jonas Kahnwald', season: 1},
                {
                  parent: 'Ines Kahnwald',
                  child: 'Mikkel Nielsen / Michael Kahnwald', season: 1,
                  type: 'adopted'
                },
                {parent: 'Daniel Kahnwald', child: 'Ines Kahnwald', season: 1},
                {parent: 'Helene Albers', child: 'Katharina Nielsen', season: 3},
                {parent: 'Hermann Albers', child: 'Katharina Nielsen', season: 3},
                {parent: 'Sebastian Kruger', child: 'Hannah Kahnwald', season: 3},
                {parent: 'Hannah Kahnwald', child: 'Silja Tiedemann', season: 3},
                {parent: 'Egon Tiedemann', child: 'Silja Tiedemann', season: 3},
                {parent: 'Egon Tiedemann', child: 'Claudia Tiedemann', season: 1},
                {parent: 'Doris Tiedemann', child: 'Claudia Tiedemann', season: 1},
                {parent: 'Claudia Tiedemann', child: 'Regina Tiedemann', season: 1},
                {parent: 'Bernd Doppler', child: 'Regina Tiedemann', season: 3},
                {parent: 'Aleksander Tiedemann', child: 'Bartosz Tiedemann', season: 1},
                {parent: 'Regina Tiedemann', child: 'Bartosz Tiedemann', season: 1},
                {parent: 'Bartosz Tiedemann', child: 'Noah', season: 3},
                {parent: 'Silja Tiedemann', child: 'Noah', season: 3},
                {parent: 'Bartosz Tiedemann', child: 'Agnes Nielsen', season: 3},
                {parent: 'Silja Tiedemann', child: 'Agnes Nielsen', season: 3},
                {parent: 'Jana Nielsen', child: 'Ulrich Nielsen', season: 1},
                {parent: 'Jana Nielsen', child: 'Mads Nielsen', season: 1},
                {parent: 'Tronte Nielsen', child: 'Ulrich Nielsen', season: 1},
                {parent: 'Tronte Nielsen', child: 'Mads Nielsen', season: 1},
                {parent: 'Agnes Nielsen', child: 'Tronte Nielsen', season: 1},
                {parent: 'The Unknown', child: 'Tronte Nielsen', season: 3},
                {parent: 'Jonas Kahnwald', child: 'The Unknown', season: 3},
                {parent: 'Martha Nielsen', child: 'The Unknown', season: 3},
                {parent: 'Leopold Tannhaus', child: 'H.G. Tannhaus', season: 3},
                {parent: 'Gustav Tannhaus', child: 'Leopold Tannhaus', season: 3},
                {parent: 'Heinrich Tannhaus', child: 'Gustav Tannhaus', season: 3},
                {parent: 'H.G. Tannhaus', child: 'Charlotte Doppler', season: 2, type: 'adopted'},
                {parent: 'H.G. Tannhaus', child: 'Marek Tannhaus', season: 3},
                {parent: 'Marek Tannhaus', child: 'Charlotte Tannhaus', season: 3},
                {parent: 'Sonja Tannhaus', child: 'Charlotte Tannhaus', season: 3},
              ],
            names: [],
        },
        martha: {
            relations: [
                {parent: 'Elisabeth Doppler', child: 'Charlotte Doppler', season: 3},
                {parent: 'Noah', child: 'Charlotte Doppler', season: 3},
                {parent: 'Charlotte Doppler', child: 'Elisabeth Doppler', season: 3},
                {parent: 'Peter Doppler', child: 'Elisabeth Doppler', season: 3},
                {parent: 'Charlotte Doppler', child: 'Franziska Doppler', season: 3},
                {parent: 'Peter Doppler', child: 'Franziska Doppler', season: 3},
                {parent: 'Helge Doppler', child: 'Peter Doppler', season: 3},
                {parent: 'Ulla Schmidt', child: 'Peter Doppler', season: 3},
                {parent: 'Greta Doppler', child: 'Helge Doppler', season: 3},
                {parent: 'Bernd Doppler', child: 'Helge Doppler', season: 3},
                {parent: 'Katharina Nielsen', child: 'Magnus Nielsen', season: 3},
                {parent: 'Ulrich Nielsen', child: 'Magnus Nielsen', season: 3},
                {parent: 'Katharina Nielsen', child: 'Martha Nielsen', season: 3},
                {parent: 'Ulrich Nielsen', child: 'Martha Nielsen', season: 3},
                {
                  parent: 'Katharina Nielsen',
                  child: 'Mikkel Nielsen / Michael Kahnwald', season: 3
                },
                {
                  parent: 'Ulrich Nielsen',
                  child: 'Mikkel Nielsen / Michael Kahnwald', season: 3
                },
                {parent: 'Daniel Kahnwald', child: 'Ines Kahnwald', season: 3},
                {parent: 'Helene Albers', child: 'Katharina Nielsen', season: 3},
                {parent: 'Hermann Albers', child: 'Katharina Nielsen', season: 3},
                {parent: 'Sebastian Kruger', child: 'Hannah Kahnwald', season: 3},
                {parent: 'Hannah Kahnwald', child: 'Silja Tiedemann', season: 3},
                {parent: 'Egon Tiedemann', child: 'Silja Tiedemann', season: 3},
                {parent: 'Egon Tiedemann', child: 'Claudia Tiedemann', season: 3},
                {parent: 'Doris Tiedemann', child: 'Claudia Tiedemann', season: 3},
                {parent: 'Claudia Tiedemann', child: 'Regina Tiedemann', season: 3},
                {parent: 'Bernd Doppler', child: 'Regina Tiedemann', season: 3},
                {parent: 'Aleksander Tiedemann', child: 'Bartosz Tiedemann', season: 3},
                {parent: 'Regina Tiedemann', child: 'Bartosz Tiedemann', season: 3},
                {parent: 'Bartosz Tiedemann', child: 'Noah', season: 3},
                {parent: 'Silja Tiedemann', child: 'Noah', season: 3},
                {parent: 'Bartosz Tiedemann', child: 'Agnes Nielsen', season: 3},
                {parent: 'Silja Tiedemann', child: 'Agnes Nielsen', season: 3},
                {parent: 'Jana Nielsen', child: 'Ulrich Nielsen', season: 3},
                {parent: 'Jana Nielsen', child: 'Mads Nielsen', season: 3},
                {parent: 'Tronte Nielsen', child: 'Ulrich Nielsen', season: 3},
                {parent: 'Tronte Nielsen', child: 'Mads Nielsen', season: 3},
                {parent: 'Agnes Nielsen', child: 'Tronte Nielsen', season: 3},
                {parent: 'The Unknown', child: 'Tronte Nielsen', season: 3},
                {parent: 'Martha Nielsen', child: 'The Unknown', season: 3},
                {parent: 'Leopold Tannhaus', child: 'H.G. Tannhaus', season: 3},
                {parent: 'Gustav Tannhaus', child: 'Leopold Tannhaus', season: 3},
                {parent: 'Heinrich Tannhaus', child: 'Gustav Tannhaus', season: 3},
                {parent: 'H.G. Tannhaus', child: 'Charlotte Doppler', season: 3, type: 'adopted'},
                {parent: 'H.G. Tannhaus', child: 'Marek Tannhaus', season: 3},
                {parent: 'Marek Tannhaus', child: 'Charlotte Tannhaus', season: 3},
                {parent: 'Sonja Tannhaus', child: 'Charlotte Tannhaus', season: 3},
            ],
        },
        origin: {
            relations: [
                {parent: 'Helge Doppler', child: 'Peter Doppler', season: 3},
                {parent: 'Ulla Schmidt', child: 'Peter Doppler', season: 3},
                {parent: 'Greta Doppler', child: 'Helge Doppler', season: 3},
                {parent: 'Bernd Doppler', child: 'Helge Doppler', season: 3},
                {parent: 'Daniel Kahnwald', child: 'Ines Kahnwald', season: 3},
                {parent: 'Helene Albers', child: 'Katharina Nielsen', season: 3},
                {parent: 'Hermann Albers', child: 'Katharina Nielsen', season: 3},
                {parent: 'Sebastian Kruger', child: 'Hannah Kahnwald', season: 3},
                {parent: 'Egon Tiedemann', child: 'Claudia Tiedemann', season: 3},
                {parent: 'Doris Tiedemann', child: 'Claudia Tiedemann', season: 3},
                {parent: 'Claudia Tiedemann', child: 'Regina Tiedemann', season: 3},
                {parent: 'Bernd Doppler', child: 'Regina Tiedemann', season: 3},
                {parent: 'H.G. Tannhaus', child: 'Marek Tannhaus', season: 3},
                {parent: 'Leopold Tannhaus', child: 'H.G. Tannhaus', season: 3},
                {parent: 'Gustav Tannhaus', child: 'Leopold Tannhaus', season: 3},
                {parent: 'Heinrich Tannhaus', child: 'Gustav Tannhaus', season: 3},
                {parent: 'Marek Tannhaus', child: 'Charlotte Tannhaus', season: 3},
                {parent: 'Sonja Tannhaus', child: 'Charlotte Tannhaus', season: 3},
              ],
        },
    };
    ['jonas', 'martha', 'origin'].forEach((world) => {
        const names = new Set();
        for (const relation of family_datasets[world].relations) {
            names.add(relation.parent);
            names.add(relation.child);
        }
        family_datasets[world].names = Array.from(names);
    });
  • ¶

    A Scale to generate colors for each name.

    var family_name_color = d3.scale.category10();
    var name_color = (fullName) => {
        var names = fullName.split(' ');
        return family_name_color(names[names.length - 1]);
    };
  • ¶

    Create Butterfly visualization

  • ¶

    Create Butterfly visualization object

    var family_tree = new c3.Sankey.Butterfly<string, Relation>({
  • ¶

    Bind to the DOM and set height.

        anchor: '#dark_tree',
        height: 800,
  • ¶

    Define unique key accessors

        key: (name) => name,
        link_key: (relation) => relation.parent + '/' + relation.child,
        link_value: () => 1,
        link_source: (relation) => relation.parent,
        link_target: (relation) => relation.child,
  • ¶

    Align tree to start on the left

        align: 'left',
  • ¶

    Overflow to the right if the nodes would become too crowded.

        overflow_width_ratio: 0.5,
    
        node_padding: '75%',
  • ¶

    Style nodes based on the name and create tooltips. Animate transitions for all of the nodes and links.

        node_options: {
            title: (name) => name,
            animate: true,
            duration: 3000,
        },
        rect_options: {
            styles: {
                fill: name => name_color(name),
            },
            animate: true,
            duration: 3000,
        },
        link_options: {
            title: (link) => link.parent + " → " + link.child,
            styles: {
                stroke: link => d3.interpolateRgb(
                    name_color(link.parent),
                    name_color(link.child),
                )(0.5),
            },
            animate: true,
            duration: 3000,
        },
        path_options: {
            styles: {
                'stroke-dasharray': link => link.type === 'adopted' ? 4 : null,
            },
            animate: true,
            duration: 3000,
        },
  • ¶

    Add text labels for each node

        node_label_options: {
            text: (name) => name,
            orientation: 'horizontal',
            animate: true,
            duration: 3000,
        },
    });
    
    function setData() {
        family_tree.data = family_datasets[world].names;
        family_tree.links = family_datasets[world].relations.filter(r => r.season <= season);
    }
    
    setData();
    family_tree.render();
  • ¶

    Extend dynamic chart behavior

  • ¶

    Resize the control flow graph when the window is resized.

    window.onresize = function () { family_tree.resize(); };
  • ¶

    Select example data set

    document.getElementById('dataset').addEventListener('change', function () {
        let element = <HTMLInputElement>this;
        world = element.value;
        setData();
        family_tree.redraw();
    });
  • ¶

    Select spoilers from season

    document.getElementById('season').addEventListener('change', function () {
        let element = <HTMLInputElement>this;
        season = +element.value;
        setData();
        family_tree.redraw();
    });
  • ¶

    Set Depth of Field

    document.getElementById('depth_of_field').addEventListener('change', function () {
        let element = <HTMLInputElement>this;
        family_tree.depth_of_field = +element.value;
        family_tree.redraw();
    });
  • ¶

    View Full Tree Button Handler

    document.getElementById('view_full_tree').addEventListener('click', function () {
        family_tree.focal = null;
        family_tree.redraw();
        return false;
    });