Subho
Subho

Reputation: 923

How to specify 3 different custom colors for 3 nodes in D3 force layout on load

I am using a D3 api to show some nodes in an order ,the nodes are coming to the page in same color.When i hover on a node then its changing into some other color. but I want a fixed color for a node (different from others) in the time of loading. I am using this api.. please help me...

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .node circle {
stroke: white;
stroke-width: 1.5px;
opacity: 1.0;
   }

line {
stroke: black;
stroke-width: 1.5px;
stroke-opacity: 1.0;
 }
 </style>
 <body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
    data = {
        nodes : [ {
            size : 10
        }, {
            size : 5
        }, {
            size : 2
        }, {
            size : 3

        } ],
        links : [ {
            source : 0,
            target : 1
        }, {
            source : 0,
            target : 2

        } ]
    }

    var mouseOverFunction = function(d,i) {
        var circle = d3.select(this);
        //$ fill = d3.scale.category20();
        node.transition(500).style("opacity", function(o) {
            return isConnected(o, d) ? 1.0 : 0.2;
        }).style("fill", function(o) {
            if (isConnectedAsTarget(o, d) && isConnectedAsSource(o, d)) {
                fillcolor = 'green';
            } else if (isConnectedAsSource(o, d)) {
                fillcolor = 'red';
            } else if (isConnectedAsTarget(o, d)) {
                fillcolor = 'blue';
            } else if (isEqual(o, d)) {
                fillcolor = "hotpink";
            } else if(isNot(o,d)){

              //here the output will shown when no mouse event is occured


            }
            return fillcolor;
        });

        link
                .transition(500)
                .style("stroke-opacity", function(o) {
                    return o.source === d || o.target === d ? 1 : 0.2;
                })
                .transition(500)
                .attr(
                        "marker-end",
                        function(o) {
                            return o.source === d || o.target === d ? "url(#arrowhead)"
                                    : "url()";
                        });

        circle.transition(500).attr("r", function() {
            return 1.4 * node_radius(d)
        });
    }

    var mouseOutFunction = function() {
        var circle = d3.select(this);

        node.transition(500);

        link.transition(500);

        circle.transition(500).attr("r", node_radius);
    }

    function isConnected(a, b) {
        return isConnectedAsTarget(a, b) || isConnectedAsSource(a, b)
                || a.index == b.index;
    }

    function isConnectedAsSource(a, b) {
        return linkedByIndex[a.index + "," + b.index];
    }

    function isConnectedAsTarget(a, b) {
        return linkedByIndex[b.index + "," + a.index];
    }

    function isEqual(a, b) {
        return a.index == b.index;
    }

        //here i have trying to make root  node a separate color and the child nodes two separate colors
    function isNot(a,b){

    return d3.scale.category20()(i);
    }


    function tick() {
        link.attr("x1", function(d) {
            return d.source.x;
        }).attr("y1", function(d) {
            return d.source.y;
        }).attr("x2", function(d) {
            return d.target.x;
        }).attr("y2", function(d) {
            return d.target.y;
        });

        node.attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
        });
    }

    function node_radius(d) {
        return Math.pow(40.0 * d.size, 1 / 3);
    }

    var width = 1000;
    var height = 500;

    var nodes = data.nodes
    var links = data.links

    var force = d3.layout.force().nodes(nodes).links(links).charge(-3000)
            .friction(0.6).gravity(0.6).size([ width, height ]).start();

    var linkedByIndex = {};
    links.forEach(function(d) {
        linkedByIndex[d.source.index + "," + d.target.index] = true;
    });

    var svg = d3.select("body").append("svg").attr("width", width).attr(
            "height", height);

    var link = svg.selectAll("line")
                .data(links)
                .enter()
                .append("line");

    var node = svg.selectAll(".node")
                .data(nodes)
                .enter()
        .append("g")
        .style("fill", function (d) {
                    return '#1f77b4';
                })  
        .attr("class", "node")
                .attr("cx",function(d) {
                    return d.x;
                })
            .attr("cy",function(d) {
                    return d.y;
                })  
        //.attr("fill",function(d){return color(d.range);})
        .call(force.drag);

    node.append("circle")
                .attr("r", node_radius)
                .on("mouseover", mouseOverFunction)
                .on("mouseout", mouseOutFunction);

    svg.append("marker")
                .attr("id", "arrowhead")
                .attr("refX", 6 + 7) // Controls the shift of the arrow head along the path
        .attr("refY", 2)
                .attr("markerWidth", 6)
                .attr("markerHeight", 4)
                .attr("orient", "auto")
                .append("path")
                .attr("d", "M 0,0 V 4 L6,2 Z");

    link.attr("marker-end", "url()");

    force.on("tick", tick);
</script>
 </body>

This is the output I got

But This is what I want..

Upvotes: 2

Views: 1025

Answers (1)

Christopher Chiche
Christopher Chiche

Reputation: 15325

I think you are nearly there with your code. On mouseover you should modify the 'fill' css property of your node.

To do so, in d3 you must use:

node.append("circle")
    .attr("r", node_radius)
    .on("mouseover",mouseOverFunction)

as you did. In moueOverFunction you have two choices to get properties for a given node:

1) Get the color from a property of the node:

function mouseoverFunction(node) {
    if(node.size<10) {
        return 'blue'
    } else {
        return 'red'
    }
}

2) Get the color from the position of the node in the data array:

function mouseoverFunction(node,index) {
    if(index<2) {
        // Will color the two first nodes blue
        return 'blue'
    } else {
        // All others will be red
        return 'red'
    }
}

In the end for your specific problem I would do:

function mouseoverFunction(node,index) {
    if(index<3) {
        return ['red','green','blue'][index]
    } else {
        // index should be less than 3
        return 'black'
    }
}

Upvotes: 1

Related Questions