vis-helper.js

/**
 * Copyright (c) 2019
 *
 * @summary d3 script for charts of AggreSet
 * @author Elitza Vasileva
 * @author Bernhard Pointner
 */

/**
 * Get width of node by selector
 * @param selector
 * @returns {number}
 */
function getWidth(selector) {
    return selector.node().getBoundingClientRect().width;
}

/**
 * Get height of node by selector
 * @param selector
 * @returns {number}
 */
function getHeight(selector) {
    return selector.node().getBoundingClientRect().height;
}

/**
 * Get visualization header
 * @param vis_container
 * @returns {void | string}
 */
function getVisHeader(vis_container) {
    return d3.select("#"+vis_container.node().id+">.vis-header");
}

/**
 * add new vis in specified parent with the dummy id
 * @param parent
 * @param dummy_id
 * @param id
 * @param title
 * @returns {void|string}
 */
function addNewVis(parent, dummy_id,id,title,subtitle) {
    let dummy = cloneDummyNode(dummy_id);
    dummy.attr("id",id);
    dummy.select(".vis-header>h3>span").text(title);
    dummy.select(".vis-header>h3>small").text(subtitle);
    dummy.select(".loading-icon").classed("hide", true);
    dummy.classed("hide", false);
    d3.select(parent).node().appendChild(dummy.node());
    d3.select(dummy_id).classed("hide", true);
    return dummy;
}

/**
 * Clone dummy node
 * @param dummy_id
 * @returns {void|string}
 */
function cloneDummyNode(dummy_id) {
    return cloneNode(dummy_id);
}

/**
 * Clone a specific node and return the node
 * @param selector
 * @returns {void | string}
 */
function cloneNode(selector) {
    var node = d3.select(selector).node();
    return d3.select(node.cloneNode(true));
}

/**
 * Calc n over k combinations
 * @param n
 * @param k
 * @returns {Array}
 */
function combinations(n, k) {
    // source: https://codereview.stackexchange.com/questions/200700/n-choose-k-combination-in-javascript
    let result= [];

    function recurse(start, combos) {
        if(combos.length === k) {
            return result.push(combos.slice());
        }
        // Check if you can actually create a combo of valid length
        // given current start number
        // For example: 5 choose 4 can't begin with [3] since it would never have 4 numbers
        if(combos.length + (n - start + 1) < k){
            return;
        }
        recurse(start + 1, combos);
        combos.push(start);
        recurse(start + 1, combos);
        combos.pop();
    }

    recurse(1, []);
    return result;
}

/**
 * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
 *
 * @param {String} text The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
 *
 * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
 */
function getTextDimension(text, font) {
    // re-use canvas object for better performance
    let canvas = getTextDimension.canvas || (getTextDimension.canvas = document.createElement("canvas"));
    let context = canvas.getContext("2d");
    context.font = font;
    let metrics = context.measureText(text);
    return {width: metrics.width, height: 16};
}

/**
 * compute and return the longest width of given text font in pixels.
 *
 * @param {String} array The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
 */
function getLongestTextDimension(array, font) {
    let dimensions = array.map(text => getTextDimension(''+text,font));
    let longest_width = Math.max.apply(null, dimensions.map(el => el.width));
    let longest_height = Math.max.apply(null, dimensions.map(el => el.height));
    return {width: longest_width, height: longest_height};
}