Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Revision 08a8fb349e580560cdc785d0860cc423c288eca8 authored by Changjian Chen on 20 May 2024, 01:45:34 UTC, committed by Changjian Chen on 20 May 2024, 01:45:34 UTC
update README
1 parent 90f314b
  • Files
  • Changes
  • 5b23d0f
  • /
  • src
  • /
  • plugins
  • /
  • treecut.js
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • directory
  • content
revision badge
swh:1:rev:08a8fb349e580560cdc785d0860cc423c288eca8
directory badge
swh:1:dir:d810d13a526521471406fb682b82d7b4a0267eab
content badge
swh:1:cnt:0fa26e218a3e791e922a0907e00ca54854bcc61c

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • revision
  • directory
  • content
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
treecut.js
function assert(flag, string){
    if (!flag){
      throw Error(string);
    }
}

const TreeCut = function (bbox_width, bbox_height, layer_height) {
    let _this = this;
    let _tree = null;
    let _tree_layout = null;
    let _bbox_width = bbox_width;
    let _bbox_height = bbox_height;
    let _layer_height = layer_height;
    let pinArray = [];
    let clickQueue = [];
    let tmp_children = {};
    clickQueue.insertAt = function (index, obj) {
        this.splice(index, 0, obj);
    };
    clickQueue.removeAt = function (index) {
        this.splice(index, 1);
    };

    this._update_tree = function(tree){
        _tree = tree;
        return true;
    };

    this._update_tree_layout = function(tree_layout){
        _tree_layout = tree_layout;
        return true;
    };

    this.candraw = function(root){
        let nodes = _tree_layout(root);
        _this.recover_children(root);
        let x1 = Number.MAX_VALUE, x2 = Number.MIN_VALUE, y1 = Number.MAX_VALUE, y2 = Number.MIN_VALUE;
        for (let i = 0; i < nodes.length; i++) {
            let node = nodes[i];
            if ((node.x - node._total_width/2) < x1) {
                x1 = node.x - node._total_width/2;
            }
            if ((node.x + node._total_width/2) > x2) {
                x2 = node.x + node._total_width/2;
            }
            if (node.y < y1) {
                y1 = node.y;
            }
            if (node.y > y2) {
                y2 = node.y;
            }
        }
        //if(max_depth === 5){
        //    console.log("candraw max_depth", max_depth, y2, y1);
        //}
        // console.log(x1, x2, y1, y2, _bbox_width, _bbox_height, _layer_height);
        if (x2 - x1 > _bbox_width || y2 - y1 > (_bbox_height - _layer_height)) {
            return false;
        }
        //if(max_depth === 5){
        //    console.log("candraw max_depth", max_depth);
        //}
        let offset = (x1 + x2) / 2 + 10;
        // console.log("offset:", offset);
        if (!offset){
            offset = 0.001;
        }
        return offset;
    };

    this.recover_children = function(source){
        let that = this;
        if(!source.children) source.children=[];
        source.all_children.forEach(d => that.recover_children(d));
    };

    this.initial = function(root){
        _this._initial(root);
    };

    this._initial = function (d) {
        // if (!d._children) {
        //     d._children = [];
        // }
        if (!d.children) {
            d.children = [];
        }
        if (!d.all_children){
            d.all_children = [];
        }
        if (!d.window) {
            d.window = {};
        }
        if (!d.window.x) {
            d.window.x = 0;
        }
        if (!d.window.y) {
            d.window.y = 0;
        }
        if (!d.beforeList) {
            d.beforeList = [];
        }
        if (!d.afterList) {
            d.afterList = [];
        }
        // let ratio = d.value_new.reduce((acc,x) => acc + x) / d.value.reduce((acc,x) => acc + x);
        //d.api = d._total_width * (1 + 10 * ratio) - 5 * Uncertainty(d);
        // d.api = 2 - (d.data.precision * 2 + d.data.recall) / 2; //Math.sqrt(d._total_width) * (1 + 10 * ratio) ;
        assert(!isNaN(d.api), "api NaN error");
        d.doi = d.api;
        d.all_children.forEach(_this._initial);
    };

    this.drawPin = function () {
        for (let i = 0; i < pinArray.length; i++) {
            _this.draw_single_node(pinArray[i]);
        }
    };

    this.update_before_after_cnt_field = function (source) {
        // update before list and after list according window.x and window.y
        source.beforeList = [];
        for (let i = source.window.x - 1; i >= 0; i--) {
            if (source.children.indexOf(source.all_children[i]) < 0) {
                source.beforeList.push(source.all_children[i]);
            }
        }
        source.afterList = [];
        for (let i = source.window.y; i < source.all_children.length; i++) {
            if (source.children.indexOf(source.all_children[i]) < 0) {
                source.afterList.push(source.all_children[i]);
            }
        }
    };

    this.update_has_after_cnt_field = function (source) {
        // initial before list and update after list
        source.beforeList = [];
        source.afterList = [];
        if (source.children.length === 0){
            return;
        }

        let min_idx = 1000, max_idx = -1;
        for (let i = 0; i < source.children.length; i++){
            let siblings_id = source.children[i].siblings_id;
            if (siblings_id > max_idx){
                max_idx = siblings_id;
            }
            if (siblings_id < min_idx){
                min_idx = siblings_id;
            }
        }
        // console.log("update has after cnt field", min_idx, max_idx, source.children.map(d => d.siblings_id), source.all_children.map(d => d.siblings_id));
        for (let i = max_idx - 1; i >= 0; i--) {
            if (source.children.indexOf(source.all_children[i]) < 0) {
                source.beforeList.push(source.all_children[i]);
            }
        }
        for (let i = max_idx + 1; i < source.all_children.length; i++) {
            // if (source.children.indexOf(source.all_children[i]) < 0) {
                source.afterList.push(source.all_children[i]);
            // }
        }

        // for (let i = 0; i < source.all_children.length; i++) {
        //     if (source.children.indexOf(source.all_children[i]) < 0) {
        //         source.afterList.push(source.all_children[i]);
        //     }
        // }
    };

    this.check_order = function(arr){
        if (arr.length < 2) return;
        for(let i = 0; i < arr.length-1; i++){
            assert(arr[i].siblings_id <= arr[i+1].siblings_id, "check order error");
        }
    };

    this.from_rest_to_children = function (node) {
        // put node to children
        if (node.parent.all_children.indexOf(node) < 0) {
            return false;
        }
        if (node.parent.children.indexOf(node) < 0) {
            let parent_id = node.parent.id;
            assert(parent_id !== undefined, "parent id error");
            tmp_children[parent_id] = node.parent.children.slice(); // shallow copy

            // // added in random
            // node.parent.children.push(node);

            // added in order
            let i = 0;
            for(; i < node.parent.children.length; i++){
                if (node.parent.children[i].siblings_id > node.siblings_id){
                    break;
                }
            }
            node.parent.children.splice(i,0,node);
            this.check_order(node.parent.children);

            // reordering(node.parent);

            this.update_has_after_cnt_field(node.parent);
            return true;
        } else {
            // assert(0, "from rest to children error");
            return false;
        }
    };

    this.from_children_to_rest = function (node) {
        let id = node.parent.children.indexOf(node);
        if (id >= 0){
            // node.parent.children.splice(id, 1);
            let parent_id = node.parent.id;
            assert(tmp_children[parent_id].length === node.parent.children.length - 1, "children length error");
            node.parent.children = tmp_children[parent_id];
            _this.update_has_after_cnt_field(node.parent);
            return true;
        }
        else{
            assert(0, "from children to rest error");
            return false;
        }
    };

    this.draw_single_node = function (source) {
        //
        let node = source;
        let drawArr = [];
        while (node != null) {
            if (node.parent != null && _this.from_rest_to_children(node)) {
                drawArr.push(node);
                node = node.parent;
            } else {
                break;
            }
        }
        if (_this.candraw(_tree)) {
            return true;
        } else {
            for (let i = 0; i < drawArr.length; i++) {
                _this.from_children_to_rest(drawArr[i]);

            }
            return false;
        }
    };

    this.searchUpY = function(source){
        for (let y = source.window.y; y < source.all_children.length; y++) {
            if (source.children.indexOf(source.all_children[y]) < 0) {
                let node = source.all_children[y];
                _this.from_rest_to_children(node);
                if (!_this.candraw(_tree)) {
                    _this.from_children_to_rest(node);
                    break;
                }
            }
            source.window.y = y + 1;
        }
    };

    this.searchDownX = function (source) {
        for (var x = source.window.x - 1; x >= 0; x--) {
            if (source.children.indexOf(source.all_children[x]) < 0) {
                var node = source.all_children[x];
                _this.from_rest_to_children(node);
                if (!_this.candraw(_tree)) {
                    _this.from_children_to_rest(node);
                    break;
                }
            }
            source.window.x = x;
        }
    }

    this.draw_my_children = function(source){
        source.window.x = 0;
        source.window.y = 0;
        _this.searchUpY(source);
    };

    this.draw_node_as_much_as_possible = function(source){
        let node = source;
        if (node != null) {
            node = node.parent;
        }
        while (node != null) {
            _this.draw_my_children(node);
            node = node.parent;
        }
    };

    this.draw_nodes_according_to_click_queue = function () {
        // try to draw as many nodes in click queue as possible
        let nodes_in_click_queue_drawn = [];
        for (let i = 0; i < clickQueue.length; i++) {
            if (_this.draw_single_node(clickQueue[i].node)){
                nodes_in_click_queue_drawn.push(clickQueue[i]);
            }
        }
        // if all nodes in click queue cannot be drawn
        if (nodes_in_click_queue_drawn.length === 0){
            _this.draw_my_children(_tree);
        }
        // try to draw the children of nodes that have been drawn
        else{
            for(let i = 0; i < nodes_in_click_queue_drawn.length; i++){
                if(nodes_in_click_queue_drawn[i].drawChildren){
                    _this.draw_my_children(nodes_in_click_queue_drawn[i].node);
                }
                _this.draw_node_as_much_as_possible(nodes_in_click_queue_drawn[i].node);
            }
        }
    };

    // this.update_rest_node = function(source){
    //     if (source.id.length > 5 && source.id.slice(0,5) === "rest-") return;
    //     if (source.beforeList.length > 0){
    //         // create a rest node
    //         let rest_node = new TreeNode();
    //         rest_node.id = "rest-before-node-" + source.id;
    //         rest_node._widthes = new Array(20).fill(0); // TODO:  20
    //         // insert the rest node in the front of children list
    //         source.children = [rest_node].concat(source.children);
    //     }
    //     if (source.afterList.length > 0){
    //         // create a rest node
    //         let rest_node = new TreeNode();
    //         rest_node.id = "rest-after-node-" + source.id;
    //         rest_node._widthes = new Array(20).fill(0); // TODO:  20
    //         // insert the rest node in the back of children list
    //         source.children.push(rest_node);
    //     }
    //     source.children.forEach(_this.update_rest_node);
    // };

    
    
    this.treeCut = function (sources, tree, tree_layout) {
        console.log("bbox width and height", bbox_width, bbox_height);
        _this._update_tree(tree);
        _this._update_tree_layout(tree_layout);
        if(!sources){
            sources = [_tree];
        }
        let source = sources[0];
        _this.initial(_tree);
        let nodes = _tree_layout(_tree).filter(d => !d.is_rest_node); // update depth
        nodes.forEach(d=>d.debug_visited=false);
        traverse_for_doi(_tree, source);
        console.log("source in treecut", sources);
        clickQueue = get_selection_array(_tree, sources);
        // console.log("clickqueue", clickQueue.map(d => d.node.name));
        pinArray = nodes.filter(d=>d.pinned);
        console.log("pinArray: ", pinArray);
        collapse(_tree);
        _this.drawPin();
        _this.draw_nodes_according_to_click_queue();
          
        // update__children(_tree);
        let offset = _this.candraw(_tree);
        assert(offset, "offset error");
        return offset;
    };

    function tree_distance(r1, r2){
        let LCA2 = function(a,b){
            while (a.depth > b.depth) {
                a = a.parent;
            }
            while (b.depth > a.depth) {
                b = b.parent;
            }
            while (a.id !== b.id) {
                a = a.parent;
                b = b.parent;
            }
            return a;
        };
        let p = LCA2(r1, r2);
        let d = r1.depth + r2.depth - p.depth * 2;
        if (r1.id != r2.id){
            d = d + 0.1 - 0.1 / (r1.order - r2.order) / (r1.order - r2.order);
        }
        if (r1.id != r2.id && r1.depth === r2.depth && r1.parent.all_children.indexOf(r2) > 0){
            console.log("siblings");
            let siblings_dis = (r1.siblings_id - r2.siblings_id) * (r1.siblings_id - r2.siblings_id);
            siblings_dis = 1 / siblings_dis / 5;
            d = d - siblings_dis;
        }
        // console.log(d, r1, r2);
        assert(d >= 0, "distance error!!!");
        if (p == r2) return d / 5;
        if (p == r2.parent) return d / 2;
        return d;
    }

    function traverse_for_doi(r, source) {
        assert(source, "source is empty");
        //calculate present doi
        // let alpha = 0.6, beta = 0.4, decay = 0.9;
        let beta = 1;
        let dis = tree_distance(r, source);
        // r.doi = r.doi * (decay * alpha + 1.0 / (dis + 1) / (dis + 1) * beta);
        r.doi = r.api + 1.0 / (dis + 1) / (dis + 1) * beta;
        assert(!isNaN(r.doi), "doi NaN error!");
        r.in_selection_array = false;
        if(!r.children){return;}
        for(let i = 0; i < r.all_children.length; i++){
            traverse_for_doi(r.all_children[i], source);
        }
    }

    function get_selection_array(root, sources) {
        let selection_array = [];
        // add selected node and his children to selection array
        let source = sources[0];
        // selection_array.push({
        //     "node": source,
        //     "drawChildren": true
        // });
        sources.forEach(d=>{
            selection_array.push({
                "node": d,
                "drawChildren": true
            });
            d.in_selection_array = true;
        });
        source.in_selection_array = true;
        for(let i = 0; i < source.all_children.length; i++){
            selection_array.push({
                "node": source.all_children[i],
                "drawChildren": true
            });
            source.all_children[i].in_selection_array = true;
        }
        // add other nodes according to doi
        let all_node = [];
        let first_not_visited_node = 0;
        all_node.push(root);
        while(first_not_visited_node !== all_node.length){
            let visited_node = all_node[first_not_visited_node];
            first_not_visited_node ++;
            if (visited_node.all_children){
                for(let i = 0; i < visited_node.all_children.length; i++ ){
                    all_node.push(visited_node.all_children[i]);
                }
            }
        }
        all_node.sort(function(a,b){return b.doi - a.doi;});
        for (let i = 0; i < all_node.length; i++){
            if (!all_node[i].in_selection_array){
                selection_array.push({
                    "node": all_node[i],
                    "drawChildren": true
                })
            }
        }

        // // remove redundant nodes in selection array: clickQueue.removeRedundantNodes in tree.cut.js
        // for (let i = selection_array.length - 1; i >= 1; i--){
        //     if (inPath(source, selection_array[i].node)){
        //         selection_array.splice(i, 1);
        //     }
        // }

        // // preserve nodes with top-k doi (optional)
        // selection_array.splice(5);

        return selection_array
    }

    function collapse(node) {
        //collapse all children, namely all children are stored in node.children and node._children is empty
        node.children = [];
        if (node.all_children.length > 0){
            node.all_children.forEach(collapse);
        }
    }

    // function update__children(node) {
    //     // update node._children according to node.children
    //     node._children = [];
    //     for (let i = 0; i < node.all_children.length; i++ ){
    //         if (node.children.indexOf(node.all_children[i]) < 0){
    //             node._children.push(node.all_children[i]);
    //         }
    //     }
    //     node.all_children.forEach(update__children);
    // }

    // function inPath(source, item) {
    //     let node = item;
    //     while (node != null) {
    //         if (node === source)
    //             return true;
    //         node = node.parent;
    //     }
    //     node = source;
    //     while (node != null) {
    //         if (node === item)
    //             return true;
    //         node = node.parent;
    //     }
    //     return false;
    // }
};

export {TreeCut}
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API