public renderGraph()

in packages/network-navigator/src/NetworkNavigator.ts [322:585]


    public renderGraph() {
        // TODO: Break this apart, this is ginormous
        if (this.graph) {
            const graph = this.graph;
            const me = this;

            this.zoom = d3.behavior.zoom()
            .scaleExtent([this._configuration.minZoom, this._configuration.maxZoom])
            .on("zoom", () => {
                const event = d3.event as d3.ZoomEvent;
                this.scale = event.scale;
                this.translate = event.translate;
                this.zoomToViewport();
                this.events.raiseEvent("zoomed", {
                    scale: this.scale,
                    translate: this.translate,
                });
            });

            const drag = d3.behavior.drag()
                .origin((d: any) => d)
                // The use of "function" is important to preserve "this"
                .on("dragstart", function(d: any) { // tslint:disable-line only-arrow-functions

                    // Stop the force graph animation while we are dragging, otherwise it causes the graph to
                    // jitter while you drag it
                    (<any>d3.event).sourceEvent.stopPropagation();
                    d3.select(this).classed("dragging", true);
                    me.force.stop();
                })
                .on("drag", function(d: any) { // tslint:disable-line only-arrow-functions
                    // While we drag, adjust the dragged node, and tell our node renderer to draw a frame
                    const evt = <any>d3.event;
                    d.px = d.x = evt.x;
                    d.py = d.y = evt.y;
                    /* tslint:disable */
                    tick();
                    /* tslint:enable */
                })
                .on("dragend", function(d: any) { // tslint:disable-line only-arrow-functions
                    d3.select(this).classed("dragging", false);

                    // If we have animation on, then start that beast
                    if (me.configuration.animate) {
                        me.force.resume();
                    }
                });

            this.svg.remove();

            this.svg = d3.select(this.svgContainer[0]).append("svg")
                .attr("width", this.dimensions.width)
                .attr("height", this.dimensions.height)
                .attr("preserveAspectRatio", "xMidYMid meet")
                .attr("pointer-events", "all")
                .call(this.zoom);
            this.vis = this.svg.append("svg:g");

            const nodes = graph.nodes.slice();
            const links: Array<{ source: any; target: any; }> = [];
            const bilinks: any[] = [];

            graph.links.forEach((graphLink) => {
                const s = nodes[graphLink.source];
                const t = nodes[graphLink.target];
                const w = graphLink.value;
                const cw = graphLink.colorValue;
                const i = {}; // intermediate node
                nodes.push(<any>i);
                links.push({ source: s, target: i }, { source: i, target: t });
                bilinks.push([s, i, t, w, cw]);
            });

            this.force.nodes(nodes).links(links);

            // If we have animation on, then start that beast
            if (this.configuration.animate) {
                this.force.start();
            }

            const determineDomain: (
                configMin: number,
                configMax: number,
                fn: (i: any) => number,
            ) => [number, number] = (
                configMin: number,
                configMax: number,
                fn: (i: any) => number,
            ) => {
                if (configMin !== undefined && configMax !== undefined) {
                    return [configMin, configMax];
                } else {
                    let min: number;
                    let max: number;
                    const data = bilinks.map(fn);
                    data.forEach((d: number) => {
                        if (min === undefined || d < min) {
                            min = d;
                        }
                        if (max === undefined || d > max) {
                            max = d;
                        }
                    });
                    return [min, max];
                }
            };

            const edgeColorWeightDomain = determineDomain(
                this.configuration.minEdgeColorWeight,
                this.configuration.maxEdgeColorWeight,
                (b) => b[4],
            );

            const edgeWidthDomain = determineDomain(
                this.configuration.minEdgeWeight,
                this.configuration.maxEdgeWeight,
                (b) => b[3],
            );

            const edgeColorScale = d3.scale.linear()
                .domain(edgeColorWeightDomain)
                .interpolate(d3.interpolateRgb as any)
                .range([
                    this.configuration.edgeStartColor,
                    this.configuration.edgeEndColor,
                ] as any);

            const edgeWidthScale = d3.scale.linear()
                .domain(edgeWidthDomain)
                .interpolate(d3.interpolateNumber as any)
                .range([
                    this.configuration.edgeMinWidth,
                    this.configuration.edgeMaxWidth,
                ]);

            const domainBound = (v: number, domain: [number, number]) => (
                Math.min(domain[1], Math.max(domain[0], v))
            );

            const xform = (v: number, scale: Function, domain: [number, number], defaultValue: any) => {
                const isValuePresent = v !== undefined;
                const boundedValue = isValuePresent ? domainBound(v, domain) : v;
                const result = isValuePresent ?
                    scale(boundedValue) :
                    defaultValue;
                return result;
            };

            this.vis.append("svg:defs").selectAll("marker")
                .data(["end"])
                .enter()
                .append("svg:marker")
                .attr("id", String)
                .attr("viewBox", "0 -5 10 10")
                .attr("refX", 15)
                .attr("refY", 0)
                .attr("markerWidth", 7)
                .attr("markerHeight", 7)
                .attr("orient", "auto")
                .append("svg:path")
                .attr("d", "M0,-5L10,0L0,5");

            const link = this.vis.selectAll(".link")
                .data(bilinks)
                .enter().append("line")
                .attr("class", "link")
                .style("stroke", function(d: any) { // tslint:disable-line only-arrow-functions
                    return xform(
                        d[4],
                        edgeColorScale,
                        edgeColorWeightDomain,
                        "gray",
                    );
                })
                .style("stroke-width", function(d: any) { // tslint:disable-line only-arrow-functions
                    return xform(
                        d[3],
                        edgeWidthScale,
                        edgeWidthDomain,
                        DEFAULT_EDGE_SIZE,
                    );
                })
                .attr("id", function(d: any) { // tslint:disable-line only-arrow-functions
                    return d[0].name.replace(/\./g, "_").replace(/@/g, "_") + "_" +
                        d[2].name.replace(/\./g, "_").replace(/@/g, "_");
                });

            const node = this.vis.selectAll(".node")
                .data(graph.nodes)
                .enter().append("g")
                .call(drag)
                .attr("class", "node");

            node.append("svg:circle")
                .attr("r", (d: any) => {
                    let width = d.value;
                    /* tslint:disable */
                    if (typeof width === "undefined" || width === null) {
                    /* tslint:enable */
                        width = DEFAULT_NODE_SIZE;
                    }
                    const maxSize = this._configuration.maxNodeSize;
                    const minSize = this._configuration.minNodeSize;
                    width  = (maxSize && width > maxSize) ? maxSize : width;
                    width = (minSize && width < minSize) ? minSize : width;
                    // Make sure > 0
                    return width > 0 ? width : 0;
                })
                .style("fill", (d: any) => d.color)
                .style("stroke", "red")
                .style("stroke-width", (d: any) => d.selected ? 1 : 0);

            node.on("click", (n: INetworkNavigatorNode) => this.onNodeClicked(n));

            node.on("mouseover", () => {
                /* tslint:disable */
                d3.select(this.svgContainer.find("svg text")[0]).style("display", null);
                /* tslint:enable */
            });
            node.on("mouseout", () => {
                if (!this._configuration.labels) {
                    d3.select(this.svgContainer.find("svg text")[0]).style("display", "none");
                }
            });

            link.append("svg:text")
                .text((d: any) => "yes")
                .attr("fill", "black")
                .attr("stroke", "black")
                .attr("font-size", () => `${this.configuration.fontSizePT}pt`)
                .attr("stroke-width", "0.5px")
                .attr("class", "linklabel")
                .attr("text-anchor", "middle");

            node.append("svg:text")
                .attr("class", "node-label")
                .text((d: any) => d.name)
                .attr("fill", (d: any) => d.labelColor || this.configuration.defaultLabelColor)
                .attr("stroke", (d: any) => d.labelColor || this.configuration.defaultLabelColor)
                .attr("font-size", () => `${this.configuration.fontSizePT}pt`)
                .attr("stroke-width", "0.5px")
                /* tslint:disable */
                .style("display", this._configuration.labels ? null : "none");
                /* tslint:enable */

            // If we are not animating, then play the force quickly
            if (!this.configuration.animate) {
                this.reflow(link, node);
            }

            // Our tick function, which actually moves the nodes on the svg based on their x/y positions
            const tick = () => {
                if (this.configuration.animate) {
                    link.attr("x1", (d) => d[0].x)
                        .attr("y1", (d) => d[0].y)
                        .attr("x2", (d) => d[2].x)
                        .attr("y2", (d) => d[2].y);
                    node.attr("transform", (d: any) => `translate(${d.x},${d.y})`);
                }
            };

            this.force.on("tick", tick);
        }
    }