读取圆弧的内外半径 - d3.js

Read inner and outer radius of arc - d3.js

我正在从事一个处理大型关系数据的可视化表示的项目。我们正在使用饼图来显示数据组件(按顺序)。由于缺少 space,我们一次只显示 10 个。

考虑以下示例:

假设我有 100 个数据组件,在给定的时间点我将只显示其中的 10 个。我使用的逻辑是,我将其他 90 个组件的开始和结束角度设置为 0(零)。对于这 10 个组件,我计算的开始和结束角度如下-

var angle = 360;
var count = 10;
if(data.length > count) angle = angle/count; //data is array of data component names
else angle = angle/data.length;

//Initially I'll be displaying first ten components
for(var i=0; i<data.length; i++){
    var startAngle = i * angle;
    var endAngle = startAngle + angle;
    var pi = = Math.PI/180;

    var arc = d3.svg.arc()
        .innerRadius(innerRadius) //dynamic value, calculated based on available space
        .outerRadius(outerRadius) //dynamic value, calculated based on available space
        .startAngle((startAngle)*pi)
        .endAngle((endAngle)*pi);

    //Hiding rest of the data components
    if(i >= count){
        arc.startAngle(0);
        arc.endAngle(0);
    }

    arcGroup.append("path")
        .attr("d", arc)
        .attr("stroke", "#2E2E2E")
        .attr("stroke-width", "1")
        .attr("fill","gold");

    var text = arcGroup.append("text")
        .attr("transform", "translate(" + arc.centroid() + ")")
        .attr("text-anchor", "middle")
        .attr("font-family","noto_sansregular")
        .attr("font-size", 40)
        .attr("font-weight","Bold")
        .attr("fill", "#000000")
        .attr("y",0)
        .style("visibility", "visible")
        .text(data[i]);

    //Hiding text of hidden arcs
    if(i >= count) text.style("visibility", "hidden");
}

然后,如果用户想要查看其余组件,我将提供两个按钮来旋转(时钟或逆时钟)内容。

如果当前视图是 -> 1、2、3、4、5、6、7、8、9、10

顺时针旋转一个单元格,结果视图应为 -> 100, 1, 2, 3, 4, 5, 6, 7, 8, 9

在这种情况下,我需要隐藏组件“10”并显示组件“100”,并移动其余单元格。为此,我只需要更改圆弧的起点和终点角度。我可以用计算出的角度创建新的圆弧对象。

这里的问题是我不知道如何获取圆弧的内外半径,它们是动态创建的。

从技术上讲,可以从 path 元素的 d 属性中检索 innerRadiusouterRadius,但需要解析 DSL 并将很乏味。这些值没有被 d3 很好地 存储在元素本身上。

因此,如果您在更新元素时​​重新计算 innerRadiusouterRadius 会更好:

function showFromIdx(firstIndex) {
  argGroup.selectAll('path')
    .data( d3.range(data.length)
             .map(function (d) { 
                return (d - firstIndex + data.length) % data.length; 
              })
    )
    .attr('d', function (d) {
      // You will have to calculate the radii again here.
      var innerRadius = foo(d), outerRadius = bar(d);
      return d3.svg.arc()
              .startAngle(i < count ? i * angle : 0)
              .endAngle(i < count ? (i + 1) * angle : 0)
              .innerRadius(innerRadius)
              .outerRadius(outerRadius)(d);
    });
}

就这样,

...
arcGroup.append("path")
    .filter(function(d) { 
        // You're Not restricted to the "filter" function
        var innerRadius = d.innerRadius()(d);
        var outerRadius = d.outerRadius()(d);
    })
    .attr("d", arc)
...

这是我编写的几个函数,用于获取使用 d3 创建的圆弧的内外半径。如果您发现代码中有错误,请告诉我。

function getInnerRadiusFromArc(arc) {
  var numbersInPattern = _getArcNumbers(arc);

  // Possibly, that's sector, so it starts from 0.
  // Or maybe that's something else.
  if (numbersInPattern.length < 4) {
    return 0;
  }

  // Getting minimum from the array.
  var innerRadius = Math.min.apply(null, numbersInPattern);

  return innerRadius;
}

function getOuterRadiusFromArc(arc) {
  var numbersInPattern = _getArcNumbers(arc);

  // Getting maximum from the array.
  var outerRadius = Math.max.apply(null, numbersInPattern);

  return outerRadius;
}

function _getArcNumbers(arc) {
  // Path description parameter, containing necessary data.
  var pathDescription = arc.getAttribute("d");

  // We need to get all patterns like A<number>,<number>.
  // RegExp source:
  // http://www.regular-expressions.info/floatingpoint.html
  const numberRegExp = /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/g;
  var arcPattern = new RegExp("A" + numberRegExp.source + "," + numberRegExp.source, "g");
  var arcParameters = pathDescription.match(arcPattern);

  var numbersInPattern = [];

  // We get all the numbers from array ["A<number>,<number>", "A<number>,<number>", ...].
  for (let parameterIndex = 0; parameterIndex < arcParameters.length; parameterIndex++) {
    let parameter = arcParameters[parameterIndex];

    let numbers = parameter.match(numberRegExp);

    if (numbers !== null) {
      numbersInPattern = numbersInPattern.concat(numbers);
    }
  }

  // Transform strings in our array to numbers.
  numbersInPattern = numbersInPattern.map(function (numberString) {
    return parseFloat(numberString);
  });

  return numbersInPattern;
}