如何将动态力导向布局转换为静态布局

时间:2016-08-02 14:19:02

标签: javascript d3.js force-layout

我是Javascript和d3库的新手。我正在开发一个项目,当图形加载到屏幕上时,我需要修复节点周围的跳舞。我假设它显示了每个节拍的节点/链接移动细节。我的节点/链接来自JSON格式的数据库,因此有时您可以获得5个节点及其关系,有时您可以获得10个(基于搜索条件下的数据库条目)。

页面周围节点的移动看起来不太好,目标是只显示最终结果。如果我注释掉force.on(“tick”) - 所有节点开始以一种非常混乱的方式出现在左上方 - 无法读取。我尝试了不同的黑客,但由于我的知识有限,我无法找到解决方案。

对此的任何帮助都将非常感激。

var fisheye = d3.fisheye.circular().radius(fishEyeRadius).distortion(fishEyeDistortionFactor);

var node, link, original_link;

function createFDR(graph) {
    //delete and recreate the svg element on each new search
    d3.select("svg").remove();
    svg = d3.select("div.background").append("svg").attr("width", "100%").attr(
                                           "height", "100%");

    //arrow marker for links. Two markers: one for L1 links and other for others
    svg.append("svg:defs").selectAll("marker")
        .data(["arrow", "arrowLevel1"])
        .enter().append("svg:marker")
        .attr("id", String)
        .attr("viewBox", "0 0 10 10")
        .attr("refX", 0)
        .attr("refY", 5)
        .attr("markerWidth", function(d) {
        return d=="arrowLevel1" ? 6 : 3;})
        .attr("markerHeight", function(d) {
        return d=="arrowLevel1" ? 6 : 4;})
        .attr("orient", "auto")
        .append("svg:path")
        .attr("d", "M 0 0 L 10 5 L 0 10 z")
        .style("fill", "#666666");

    //read nodes and links and associate with force
    var nodeMap = {};
    graph.nodes.forEach(function(x) {
        nodeMap[x.id] = x;
    });
    graph.links = graph.links.map(function(x) {
        return {
        source: nodeMap[x.source],
        target: nodeMap[x.target],
        value: x.value,
        info: x.info
        };
    });

    force.nodes(graph.nodes).links(graph.links).start();

    //simulate first node click event after 2s
    setTimeout(simulateClick, 2000);

    addNodeElements(graph);
    addLinkElements(graph);

    createAdjacencyList();

    //findSectorAngles(center);

    //force layout simulation. Defines what happens with each simulation tick
    force.on("tick", function(e) {
        tickCount++;

        //place level1 (L1) nodes in circular fashion around the center node
        seedInitialPlacementofL1Nodes(center);

        center_node_x = (node[0][center].__data__.x ? node[0][center].__data__.x : 0);
        center_node_y = (node[0][center].__data__.y ? node[0][center].__data__.y : 0);

        // calculate tmpOffset position for center node. Makes the transition of center node to Center of the Visualization field
        if (tickCount < 200) {
        tmpOffset.x -= (clickedNodePos.x - offsetX) / 100;
        tmpOffset.y -= (clickedNodePos.y - offsetY) / 100;
        }
        //offset each node w.r.t center node which is at tmpOffset
        node.each(function(d) {
            d.x = d.x - center_node_x + tmpOffset.x;
            d.y = d.y - center_node_y + tmpOffset.y;
        });

        //if(tickCount<200)seedInitialPlacementofL2Nodes(true);
        //if(tickCount==200)seedInitialPlacementofL2Nodes(false);

        //Move the map to the left end if the nodes are going beyond the Visualization Pane boundary
        adjustMapHorizontally(tickCount);

        // update node size and nodes' fisheye parameter after fisheye distortion
        addFisheyeDistortion(center_node_x, center_node_y);

        //update the node and link positions
        updateNodePositions();
        updateLinksPositions();

    });
}

function simulateClick() {
    first = 0;
    click(node[0][center].__data__, 0);
    force.start();
}

function seedInitialPlacementofL1Nodes(center) {
    // find the number of groups
    var i;
    noOfGroups = 0;
    for (i = 0; i < adjacencyList[center].size; i++) {
        curL1NodeIndex = adjacencyList[center].connections[i].node_index;
        if (node[0][curL1NodeIndex].__data__.L1Group > noOfGroups) {
            noOfGroups = node[0][curL1NodeIndex].__data__.L1Group;
        }
    }

    // place each group elements together;
    nodeNumber = 0;
    theta = 2 * Math.PI / adjacencyList[center].size;

    for (i = 1; i <= noOfGroups; i++) {
        for (var j = 0; j < adjacencyList[center].size; j++) {
            curL1NodeIndex = adjacencyList[center].connections[j].node_index;
            groupNumber = node[0][curL1NodeIndex].__data__.L1Group;
            if (groupNumber == i) {
        //var edgeAngle = getAngle(node[0][curL1NodeIndex].__data__.x - node[0][center].__data__.x, node[0][curL1NodeIndex].__data__.y - node[0][center].__data__.y);
                //var r = ( (edgeAngle < Math.PI/8 || edgeAngle > 15*Math.PI/8) || (edgeAngle > 7*Math.PI/8 && edgeAngle < 9*Math.PI/8) ) ? (node[0][center].__data__.textWidth/2 + l1EdgeLength) : l1EdgeLength;
        node[0][curL1NodeIndex].__data__.x = node[0][center].__data__.x + (l1EdgeLength) * Math.cos(3 * Math.PI / 2 + nodeNumber * (theta + randDeviation));
                node[0][curL1NodeIndex].__data__.y = node[0][center].__data__.y + (l1EdgeLength) * Math.sin(3 * Math.PI / 2 + nodeNumber * (theta + randDeviation));
                //console.log(node[0][curL1NodeIndex].__data__.name + ", "+ nodeNumber + ", i:" + i + "groupNumeber: " + groupNumber);
                nodeNumber++;
                //d3.select(node[0][curL1NodeIndex]).classed("fixed", node[0][curL1NodeIndex].__data__.fixed = true);
            }
        }
    }
    //console.log(nodeNumber);
}

function adjustMapHorizontally(tickCount){
    var minX = VizWidth,
    maxX = 0;
    // adjust horizontally 
    if (tickCount > 150) {
    node.each(function(d) {
        if (!d.invisible) {
            if (d.x + d.textWidth / 2 > maxX) maxX = d.x + d.textWidth;
            if (d.x - d.textWidth / 2 < minX) minX = d.x - d.textWidth;
        }
        });

    //console.log("max: " + maxX + ", " + document.getElementById("background").offsetWidth + ", " + minX);
    if (minX < 10 || maxX > VizWidth) {
        node.each(function(d) {
            d.x = d.x - minX + 10;
        });
    }
    }
}

function addFisheyeDistortion(center_node_x, center_node_y){
    fisheye.focus([center_node_x, center_node_y]);
    node.each(function(d) {
        d.fisheye = fisheye(d);
    });

    //update node height and width based on levels and fisheye distortion
    node.selectAll("text").each(function(d) {
        d.textWidth = this.getBBox().width;
        d.textHeight = this.getBBox().height;
    });

    node.selectAll("rect").attr("width", function(d) {
        return Math.max(d.fisheye.z * l1NodeWidth*WindowScaleFactor, d.textWidth * 1.2);
    }).attr("height", function(d) {
        return d.fisheye.z * l1NodeHeight*WindowScaleFactor;
        }).attr("rx", function(d) {
            return Math.max(d.fisheye.z * l1NodeWidth*WindowScaleFactor, d.textWidth * 1.2) * 0.5;
        }).attr("ry", function(d) {
            return d.fisheye.z * l1NodeHeight*WindowScaleFactor * 0.5;
            });
}

function updateNodePositions(){
    node.attr("transform", function(d) {
        return "translate(" + (d.fisheye.x - Math.max(d.fisheye.z * l1NodeWidth*WindowScaleFactor, d.textWidth * 1.2) / 2) + "," + (d.fisheye.y - (d.fisheye.z * l1NodeHeight*WindowScaleFactor) / 2) + ")";
    });

    node.selectAll("text").attr("transform", function(d) {
        return "translate(" + Math.max(d.fisheye.z * l1NodeWidth*WindowScaleFactor, d.textWidth * 1.2) / 2 + "," + (d.fisheye.z * l1NodeHeight*WindowScaleFactor + d.textHeight / 2) / 2 + ")";
    }).style("font-family", function(d) {
        return "Arial";
        }).style("font-size", function(d) {
            return (d.level == 0) ? 25*WindowScaleFactor+"px" : 15*WindowScaleFactor+"px";
        }).style("font-style", function(d) {
            return (d.level == 0) ? "italic" : "normal";
            });
}

function updateLinksPositions(){
    // find the intersection of the Links with the Rectangle and make x1,y1 and x2,y2 so that it is from boundary of the source to that of the target. 
    link.attr("x1", function(d) {
        var width = Math.max(d.source.fisheye.z * l1NodeWidth*WindowScaleFactor, d.source.textWidth * 1.2);
        var height = d.source.fisheye.z * l1NodeHeight*WindowScaleFactor;
        var boxAngle = getAngle(width, height);
        var edgeAngle = getAngle(d.target.fisheye.x - d.source.fisheye.x,
                     d.source.fisheye.y - d.target.fisheye.y);

        var x1, y1, x2, y2;
        var signFactorY = (edgeAngle < Math.PI) ? 1 : -1;
        var signFactorX = (edgeAngle < Math.PI / 2 || edgeAngle > 3 * Math.PI / 2) ? 1 : -1;
        if ((edgeAngle > boxAngle && edgeAngle < Math.PI - boxAngle) || (edgeAngle > Math.PI + boxAngle && edgeAngle < 2 * Math.PI - boxAngle)) {
        y1 = height / 2;
        x1 = Math.abs(y1 / Math.tan(edgeAngle));
        } else {
        x1 = width / 2;
        y1 = Math.abs(x1 * Math.tan(edgeAngle));
        }

        return d.source.fisheye.x + signFactorX * x1;

    }).attr("y1", function(d) {
        var width = Math.max(d.source.fisheye.z * l1NodeWidth*WindowScaleFactor, d.source.textWidth * 1.2);
        var height = d.source.fisheye.z * l1NodeHeight*WindowScaleFactor;
        var boxAngle = getAngle(width,
                    height);
        var edgeAngle = getAngle(
                     d.target.fisheye.x - d.source.fisheye.x,
                     d.source.fisheye.y - d.target.fisheye.y);
        var x1, y1, x2, y2;
        var signFactorY = (edgeAngle < Math.PI) ? 1 : -1;
        var signFactorX = (edgeAngle < Math.PI / 2 || edgeAngle > 3 * Math.PI / 2) ? 1 : -1;
        if ((edgeAngle > boxAngle && edgeAngle < Math.PI - boxAngle) || (edgeAngle > Math.PI + boxAngle && edgeAngle < 2 * Math.PI - boxAngle)) {
            y1 = height / 2;
            x1 = Math.abs(y1 / Math.tan(edgeAngle));
        } else {
            x1 = width / 2;
            y1 = Math.abs(x1 * Math.tan(edgeAngle));
        }
        return d.source.fisheye.y - signFactorY * y1;

        }).attr("x2", function(d) {
            var width = Math.max(d.target.fisheye.z * l1NodeWidth*WindowScaleFactor, d.target.textWidth * 1.2);
            var height = d.target.fisheye.z * l1NodeHeight*WindowScaleFactor;
            var boxAngle = getAngle(width, height);
            var edgeAngle = getAngle(d.target.fisheye.x - d.source.fisheye.x, d.source.fisheye.y - d.target.fisheye.y);
            var x1, y1, x2, y2;
            var signFactorY = (edgeAngle < Math.PI) ? 1 : -1;
            var signFactorX = (edgeAngle < Math.PI / 2 || edgeAngle > 3 * Math.PI / 2) ? 1 : -1;
            if ((edgeAngle > boxAngle && edgeAngle < Math.PI - boxAngle) || (edgeAngle > Math.PI + boxAngle && edgeAngle < 2 * Math.PI - boxAngle)) {
            y1 = height / 2;
            x1 = Math.abs(y1 / Math.tan(edgeAngle));
            } else {
            x1 = width / 2;
            y1 = Math.abs(x1 * Math.tan(edgeAngle));
            }
            return d.target.fisheye.x - signFactorX * x1;

        }).attr("y2", function(d) {
            var width = Math.max(d.target.fisheye.z * l1NodeWidth*WindowScaleFactor, d.target.textWidth * 1.2);
            var height = d.target.fisheye.z * l1NodeHeight*WindowScaleFactor;
            var boxAngle = getAngle(width, height);
            var edgeAngle = getAngle(d.target.fisheye.x - d.source.fisheye.x, d.source.fisheye.y - d.target.fisheye.y);
            var x1, y1, x2, y2;
            var signFactorY = (edgeAngle < Math.PI) ? 1 : -1;
            var signFactorX = (edgeAngle < Math.PI / 2 || edgeAngle > 3 * Math.PI / 2) ? 1 : -1;
            if ((edgeAngle > boxAngle && edgeAngle < Math.PI - boxAngle) || (edgeAngle > Math.PI + boxAngle && edgeAngle < 2 * Math.PI - boxAngle)) {
                y1 = height / 2;
                x1 = Math.abs(y1 / Math.tan(edgeAngle));
            } else {
                x1 = width / 2;
                y1 = Math.abs(x1 * Math.tan(edgeAngle));
            }
            return d.target.fisheye.y + signFactorY * y1;

            });

    link.selectAll("text").text(function(d) {
        return d.invisible ? "" : (d.info);
    }).attr("x", function(d) {
        var edgeAngle = getAngle(d.target.fisheye.x - d.source.fisheye.x, d.source.fisheye.y - d.target.fisheye.y);
        var length = Math.sqrt( Math.pow(d.target.fisheye.x - d.source.fisheye.x,2) + Math.pow(d.source.fisheye.y - d.target.fisheye.y,2));
        return d.source.fisheye.x + 0.6*length*Math.cos(edgeAngle);
        //d.source.fisheye.x + d.target.fisheye.x / 2;
        }).attr("y", function(d) {
            var edgeAngle = getAngle(d.target.fisheye.x - d.source.fisheye.x, d.source.fisheye.y - d.target.fisheye.y);
            var length = Math.sqrt( Math.pow(d.target.fisheye.x - d.source.fisheye.x,2) + Math.pow(d.source.fisheye.y - d.target.fisheye.y,2));
            return d.source.fisheye.y - 0.6*length*Math.sin(edgeAngle);
            //return (d.source.fisheye.y + d.target.fisheye.y) / 2;
        }).attr("text-anchor", "start")
    .attr("fill", function(d) {
        return "black";
        });
}

0 个答案:

没有答案