在三角形内部的力图d3.js [英] Force chart d3.js inside a triangle

查看:109
本文介绍了在三角形内部的力图d3.js的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在学习d3.js力图,我有一个问题。 是否可以在有一些坐标的三角形内制作力图?



这是我的代码:

  var width = 500; 
var height = 500;
// margin
var marginLeft = 10;
var marginTop = 10;
var marginRight = 10;
var marginBottom = 10;
var margin = {left:marginLeft,top:marginTop,right:marginRight,bottom:marginBottom};

// canvas的大小
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;

var radius = 10;

var svg = d3.select(。forcechart)。append(svg)
.attr(width,width)
.attr ,height)
.style(background,#eee);

var tr = svg.append(polygon)//附加一个多边形
.style(stroke,black)//为行添加颜色
.style (fill,none)//删除任何填充颜色
.attr(points,250,0,12,173,250,250); // x,y points

var group = svg.append(g)
.attr(transform,translate(+ margin.left +,+ margin .top +));

var graph = {
nodes:[{x:0,y:0},
{x:100,y 100,
{x:500,y:500},
{x:300,y:0},
{ y:0},
{x:100,y:100},
{x:500,y:500},
{x :300,y:0},
{x:300,y:0},
{x:100,y:100},
{x:500,y:500},
{x:300,y:0},
{x:300,y:0} ,
{x:100,y:100},
{x:500,y:500},
{x:300,y :0},
{x:300,y:0},
{x:100,y:100},
{x 500,y:500},
{x:300,y:0},
{x:300,y:0} ,
links:[]
};


var nodes = graph.nodes,
links = graph.links;

var force = d3.layout.force()
.size([innerWidth,innerHeight])
.nodes(nodes)
.links(links);

force.linkDistance(100);
force.charge(-200);

var link = group.selectAll('。link')
.data(links)
.enter()。append('line')
.attr ('class','link');

var node = group.selectAll('。node')
.data(nodes)
.enter()。append('circle')
.attr ('class','node');

force.on('tick',function(){
node.attr('r',radius)
.attr('cx',function(d){ return dx;})
.attr('cy',function(d){return dy;});

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;});

});


force.start();

完整代码在这里: http://codepen.io/Balzzac/pen/vGWXdQ 。现在它是一个力图在组,我需要使它是三角形tr,所以没有一个节点在我的三角形的边界之外。



谢谢



PS对不起,我的英语=)

解决方案

这个问题真的有两个部分。首先,您需要您的强制布局收敛于不同的焦点,然后默认 width / 2 ,height / 2 。这个新的焦点应该是三角形的质心。幸好 d3 助手方法为多边形计算此值。第二,现在我们正在收敛在三角形的中心,我们如何将我们的节点绑定在那个三角形内。下面使用的方法计算从质心到节点绘制的线和三角形边缘线之间的交点(从问题)。所有3条边上没有交叉点,意味着该圆在三角形中,任何边上的交叉点意味着我们需要将圆放到边上。



UPDATE - 将代码推广一点,并将其转换为阻止此处使用

让我们来看看代码:



 <!DOCTYPE html>< html>< head> < script data-require =d3@3.5.3data-semver =3.5.3src =// cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js\"> < / script>< / head>< body> < script> var width = 500,height = 500,radius = 10; var svg = d3.select(body)。append(svg).attr(width,width).attr(height,height).style(background,#eee); //我们的多边形var trianglePoints = [[250,0],[12,173],[250,250]]; var tr = svg.append(polygon)//附加一个多边形.style(stroke,black)//颜色线.style(fill,none)//删除任何填充颜色。 attr(points,trianglePoints.join()); // x,y points var group = svg.append(g); var nodes = d3.range(20).map(function(d){return {}}),links = [],cent = d3.geom.polygon(trianglePoints).centroid var force = d3.layout.force().size([width,height]).nodes(nodes).links(links); force.linkDistance(100);强力充电(-200); var link = group.selectAll('。link').data(links).enter()。append('line').attr('class','link'); var node = group.selectAll('。node').data(nodes).enter()。append('circle').attr('class','node').call(force.drag); //<  - 使它们可以拖动到测试force.on('tick',function(e){node.attr('r',radius).attr('transform',function(d){// change focus到三角形的中心var x =(dx-(width / 2-cent [0])),y =(dy-(height / 2-cent [1])) i = getLineIntersection(trianglePoints [0] [0],trianglePoints [0] [1],trianglePoints [1] [0],trianglePoints [1] [1],cent [0],cent [1],x,y) || getLineIntersection(trianglePoints [1] [0],trianglePoints [1] [1],trianglePoints [2] [0],trianglePoints [2] [1],cent [0],cent [1],x,y) || getLineIntersection(trianglePoints [0] [0],trianglePoints [0] [1],trianglePoints [2] [0],trianglePoints [2] [1],cent [0],cent [1],x,y) || false; //设置为交集if(i){x = ix; y = iy;} returntranslate(+ x +,+ y +) }); .attr('x1',function(d){return d.source.y;}).attr('x2',function(d){return d.source.x; function(d){return d.target.x;}).attr('y2',function(d){return d.target.y;}); }); // from http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect function getLineIntersection(p0_x,p0_y,p1_x,p1_y,p2_x,p2_y,p3_x,p3_y ){var s1_x,s1_y,s2_x,s2_y; s1_x = p1_x-p0_x; s1_y = p1_y-p0_y; s2_x = p3_x-p2_x; s2_y = p3_y-p2_y; var s,t; s =(-s1_y *(p0_x-p2_x)+ s1_x *(p0_y-p2_y))/(-s2_x * s1_y + s1_x * s2_y) t =(s2_x *(p0_y-p2_y)-s2_y *(p0_x-p2_x))/(-s2_x * s1_y + s1_x * s2_y); if(s> = 0&& s <= 1& t> = 0& t <= 1){var intX = p0_x +(t * s1_x); var intY = p0_y +(t * s1_y); return {x:intX,y:intY}; } return false; }    开始生效(); < / script>< / body>< / html>  


I'm studying d3.js force chart and I have a question. Is it possible to make a force chart inside a triangle with some coordinates?

Here is my code:

var width = 500;
var height = 500;
//margin
var marginLeft = 10;
var marginTop = 10;
var marginRight = 10;
var marginBottom = 10;
var margin = { left: marginLeft , top: marginTop, right: marginRight, bottom: marginBottom};

//size of canvas
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;

var radius = 10;

var svg = d3.select(".forcechart").append("svg")
    .attr("width", width) 
    .attr("height", height)
    .style("background", "#eee");

var tr = svg.append("polygon")       // attach a polygon
    .style("stroke", "black")  // colour the line
    .style("fill", "none")     // remove any fill colour
    .attr("points", "250,0, 12,173,  250,250");  // x,y points 

var group = svg.append("g")
    .attr("transform",  "translate(" + margin.left + "," + margin.top + ")");

var graph = {
    "nodes": [  { "x": 0, "y": 0 },
                { "x": 100,  "y":  100 },
                { "x": 500, "y": 500 },
                     { "x": 300, "y": 0 },
                   { "x": 300, "y": 0 },
                  { "x": 100,  "y":  100 },
                { "x": 500, "y": 500 },
                     { "x": 300, "y": 0 },
                   { "x": 300, "y": 0 },
                  { "x": 100,  "y":  100 },
                { "x": 500, "y": 500 },
                     { "x": 300, "y": 0 },
                   { "x": 300, "y": 0 },
                  { "x": 100,  "y":  100 },
                { "x": 500, "y": 500 },
                     { "x": 300, "y": 0 },
                   { "x": 300, "y": 0 },
                  { "x": 100,  "y":  100 },
                { "x": 500, "y": 500 },
                     { "x": 300, "y": 0 },
                   { "x": 300, "y": 0 },
            ],
    "links": []
    };


var nodes = graph.nodes,
    links = graph.links;

var force = d3.layout.force()
    .size([innerWidth, innerHeight])
    .nodes(nodes)
    .links(links);

force.linkDistance(100);
force.charge(-200);

var link = group.selectAll('.link')
    .data(links)
    .enter().append('line')
    .attr('class', 'link');

var node = group.selectAll('.node')
    .data(nodes)
    .enter().append('circle')
    .attr('class', 'node');

force.on('tick', function() {
    node.attr('r', radius)
        .attr('cx', function(d) { return d.x; })
        .attr('cy', function(d) { return d.y; });

    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; });

});


force.start();

Full code is here: http://codepen.io/Balzzac/pen/vGWXdQ. Now it is a force chart inside "group", I need to make it is inside triangle "tr" so no one node is outside of boundaries of my triangle.

Thanks for help!

PS Sorry for my English =)

解决方案

There's really two parts to this question. First, you need your force layout to converge on a different foci then the default width/2, height/2. This new foci should be the centroid of the triangle. Luckily d3 has a helper method to compute this for polygons. Second, now that we are converging on the centroid of the triangle, how do we bound our nodes inside that triangle. The method I use below calculates the intersections between lines drawn from the centroid to the node and the line of the edge of the triangle (intersection calculation from this question). No intersections on all 3 edges means the circle is in the triangle, and an intersection on any edge means we need to bring the circle onto that edge.

UPDATE -- generalized the code a bit and converted it to a block here to work with N sided polygons.

Let's get to the code:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>

<body>
  <script>
    var width = 500,
        height = 500,
        radius = 10;

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

    // our polygon
    var trianglePoints = [
      [250, 0],
      [12, 173],
      [250, 250]
    ];

    var tr = svg.append("polygon") // attach a polygon
      .style("stroke", "black") // colour the line
      .style("fill", "none") // remove any fill colour
      .attr("points", trianglePoints.join(" ")); // x,y points 

    var group = svg.append("g");

    var nodes = d3.range(20).map(function(d){ return {} }),
      links = [],
      cent = d3.geom.polygon(trianglePoints).centroid();

    var force = d3.layout.force()
      .size([width, height])
      .nodes(nodes)
      .links(links);

    force.linkDistance(100);
    force.charge(-200);

    var link = group.selectAll('.link')
      .data(links)
      .enter().append('line')
      .attr('class', 'link');

    var node = group.selectAll('.node')
      .data(nodes)
      .enter().append('circle')
      .attr('class', 'node')
      .call(force.drag); //<-- make them draggable to test

    force.on('tick', function(e) {

      node.attr('r', radius)
        .attr('transform', function(d) {

          // change focus to the center of the triangle
          var x = (d.x - (width / 2 - cent[0])),
            y = (d.y - (height / 2 - cent[1]));

          // test intersections on all 3 edges
          var i = 
            getLineIntersection(trianglePoints[0][0], trianglePoints[0][1],
              trianglePoints[1][0], trianglePoints[1][1], cent[0], cent[1], x, y) ||
            getLineIntersection(trianglePoints[1][0], trianglePoints[1][1],
              trianglePoints[2][0], trianglePoints[2][1], cent[0], cent[1], x, y) ||
            getLineIntersection(trianglePoints[0][0], trianglePoints[0][1],
                trianglePoints[2][0], trianglePoints[2][1], cent[0], cent[1], x, y) ||
            false;
          
          // set it to intersection
          if (i){
            x = i.x;
            y = i.y;
          }

          return "translate(" + x + "," + y + ")";

        });

      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;
        });

    });

    // from http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
    function getLineIntersection(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) {
      var s1_x, s1_y, s2_x, s2_y;
      s1_x = p1_x - p0_x;
      s1_y = p1_y - p0_y;
      s2_x = p3_x - p2_x;
      s2_y = p3_y - p2_y;
      var s, t;
      s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
      t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

      if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
        var intX = p0_x + (t * s1_x);
        var intY = p0_y + (t * s1_y);
        return {
          x: intX,
          y: intY
        };
      }
      return false;
    }

    force.start();
  </script>
</body>

</html>

这篇关于在三角形内部的力图d3.js的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆