https://github.com/thu-vis/MutualDetector
Raw File
Tip revision: c1059af41f4a6627cec3b5094096e6635f57673c authored by unknown on 11 January 2022, 04:44:40 UTC
remove video
Tip revision: c1059af
layout_text.js
import * as d3 from "d3"
import {TreeCut} from "./treecut"
 
const exit_type = function(d){
    let p = d;
    let child_p = null;
    // 0: collapsing to parent
    // 1: stay till
    // 2: collapsing to beforelist
    // 3: collapsing to afterlist
    let type = -1;
    let translate = "";
    while(p.parent && p.parent.children.indexOf(p) < 0){
        child_p = p;
        p = p.parent;
    }
    if (p.children.length === 0){
        type = 0;
        translate = "translate(" + p.prev_x + ", " + p.prev_y + ")";
    }
    else if (child_p && p.children.length !== 0){
        if (child_p.in_before_list){
            type = 2;
            translate = "translate(" + p.before_node.x + ", " + p.before_node.y + ")";
        }
        else if (child_p.in_after_list){
            type = 3;
            translate = "translate(" + p.after_node.x + 
                ", " + p.after_node.y + ")";
        }
    }
    else{
        type = 1;
        translate = "translate(" + p.prev_x + ", " + p.prev_y + ")";
    }
    return [type, translate]
}

const mini_tree_layout = function(Size){
    let that = this;
    that.Size = Size;
    
    this.layout = function(data){
        // backup state
        data.all_descendants.forEach(d => {
            d.backup_children = d.children;
            d.children = d.all_children;
            if (d.children && d.children.length === 0) d.children = undefined;
            d.backup_x = d.x;
            d.backup_y = d.y;
        });

        // tree layout
        data = d3.tree()
        .size(that.Size)(data);
        
        let nodes = data.descendants();
        let links = data.links();

        // restore state 
        data.descendants().forEach(d => {
            d.mini_x = d.x;
            d.mini_y = d.y;
            d.x = d.backup_x;
            d.y = d.backup_y;
            d.children = d.backup_children;
            d.mini_selected = false;
        });

        // set selected state
        data.descendants().forEach(d => d.mini_selected = true);

        return {nodes, links};
    };
};

const tree_layout = function(nodeSize, layout_height){
    let that = this;
    that.nodeSize = nodeSize;
    that.x_delta = nodeSize[0];
    that.y_delta = nodeSize[1];
    that.layout_height = layout_height;
    that.max_num = parseInt(that.layout_height / that.y_delta);

    this.layout = function(data, expand_tree){
        if (expand_tree === false){
            return that._aligned_layout(data);
        }
        else{
            return that._layout(data);
        }
    };

    this.update_layout_by_num = function(num){
        if (num < that.max_num){
            that.y_delta = that.layout_height / num;
        }
        else{
            that.y_delta = that.nodeSize[1];
        }
    };

    this.reset_layout = function(){
        that.y_delta = that.nodeSize[1];
    };
    this.layout_with_nodes = function(data, expand_tree){
        if (expand_tree === false){
            return that._aligned_layout(data);
        }
        else{
            // const root = that._layout(data);
            return that._layout(data).filter(d => !d.is_rest_node);
        }
    };

    this.layout_with_rest_node = function(data, expand_tree){
        if (expand_tree === false){
            return that._aligned_layout(data);
        }
        else{
            // const root = that._layout(data);
            return that._layout(data);
        }
    }

    this._aligned_layout = function(data){
        data.descendants().forEach(d => {
            if (!d.children) d.children = [];
        });
        let nodes = data.descendants().filter(d => d.children.length === 0);
        // let y_delta = that.y_delta * (data.descendants().length - 1) / nodes.length;
        let y_delta = that.layout_height / (nodes.length);
        console.log("y_delta in aligned_layout", y_delta);
        let layer = 0;
        data.eachBefore(d => {
            if (d.children.length === 0){
                d.y = layer * y_delta;
                layer += 1;
            }
        })
        nodes.forEach((d) => {
            d.x = 0;
            d.link_x = 0;
            d.link_top = that.y_delta / 2;
            d.link_bottom = d.link_top;
        });
        return nodes;
    }

    this._layout = function(data){
        data.all_descendants.forEach(d => {
            d.is_rest_node = false;
            d.in_before_list = false;
            d.in_after_list = false;
        });
        let visible_nodes = data.descendants();
        visible_nodes.forEach(d => {
            d.tmp_children = d.children.slice();
            if (d.beforeList.length > 0){
                d.beforeList.forEach(n => n.in_before_list = true);
                let rest_node = {};
                rest_node.id = "rest-before-" + d.id;
                rest_node.is_rest_node = true;
                rest_node.rest_children = d.beforeList;
                rest_node.parent = d;
                rest_node.depth = d.depth + 1;
                rest_node.data = {};
                rest_node.data.precision = 1;
                rest_node.data.recall = 1;
                rest_node.siblings_id = -1;
                rest_node.prev_vis = false;
                d.beforeList.forEach(n => {
                    if (n.prev_vis) rest_node.prev_vis=true;
                })
                d.children.push(rest_node);
                d.before_node = rest_node;

            }
            if (d.afterList.length > 0){
                d.afterList.forEach(n => n.in_after_list = true);
                let rest_node = {};
                rest_node.id = "rest-after-" + d.id;
                rest_node.is_rest_node = true;
                rest_node.rest_children = d.afterList;
                rest_node.parent = d;
                rest_node.depth = d.depth + 1;
                rest_node.data = {};
                rest_node.data.precision = 1;
                rest_node.data.recall = 1;
                rest_node.siblings_id = 1000;
                rest_node.prev_vis = false;
                d.afterList.forEach(n => {
                    if (n.prev_vis) rest_node.prev_vis=true;
                })
                d.children.push(rest_node);
                d.after_node = rest_node;
            }
            d.children.sort(function(a,b){return a.siblings_id - b.siblings_id;})
        });

        data.eachBefore((d,i) => {
            d.x = (d.depth - 1) * that.x_delta;
            d.y = (i - 1) * that.y_delta;
        });
        data.descendants().forEach(d => {
            if (!d.children) d.children = [];
            // if (d.parent){
            //     d.parent_x = d.parent.x;
            //     d.parent_y = d.parent.y;
            // }
        })
        // calculate node link 
        data.descendants().forEach(d => {
            d.link_x = 0;
            d.link_top = that.y_delta / 2;
            if (d.children && d.children.length > 0){
                d.link_bottom = d.link_top + (d.descendants().length - 1) * that.y_delta;
            }
            else{
                d.link_bottom = d.link_top;
            }
        })
        // calculate node type
        data.descendants().forEach(d => {
            let type = -1;
            if (!d.all_children || d.all_children.length===0){
                type = 2;
            }
            else{
                if (!d.children || d.children.length===0){
                    type = 0;
                }
                else{
                    type = 1;
                }
            }
            d.type = type;
        });
        let nodes = data.descendants().filter(d => d.name !== "root");
        nodes.filter(d => d.is_rest_node).forEach(d => {
            d.rest_children.forEach(n => {n.x = d.x; n.y = d.y;})
            d.rest_children = d.rest_children.slice(0, 3); // show max 3 rest children
            let children_num = d.rest_children.length;
            let single_height = that.y_delta * 0.6;
            let max_delta = that.y_delta * 0.2;
            let total_height = single_height + max_delta * (children_num - 1);
            if (total_height > that.y_delta * 0.8){
                let delta = (this.y_delta * 0.8 - single_height) / (children_num - 1);
                d.rest_children.forEach((e,i) => {
                    e.x_delta = i * 4;
                    e.y_delta = - that.y_delta * 0.8 / 2 + i * delta;
                    e.is_rest_node = true;
                })
            }
            else{
                d.rest_children.forEach((e,i) =>{
                    e.x_delta = i * 4;
                    e.y_delta = i * max_delta - total_height / 2;
                })
            }
            d.rest_children.forEach((e,i) => {
                e.last_rest_children = i === (d.rest_children.length - 1);
            })
        })
        visible_nodes.forEach(d => {d.children = d.tmp_children});
        return nodes;
    }

}

export {TreeCut, tree_layout, mini_tree_layout, exit_type}
back to top