过渡轮廓线来自contourDensity()

时间:2017-04-23 13:53:51

标签: javascript d3.js

我有一个基于散点数据的轮廓线图,并使用d3.js中的contourDensity()。更新数据集时,我想平滑地转换状态之间的轮廓线。我知道这可以使用单行,但如何将其应用于contourDensity()创建的路径?

情节示例:



var data = [];

var svg = d3.select( 'svg' );
var width = parseInt( svg.attr( 'width' ) );
var height = parseInt( svg.attr( 'height' ) );

function updateData() {
  for( var i = 0; i < 50; i++ ) {
    data[i] = {
      x: Math.random() * 800,
      y: Math.random() * 600
    }
  }
}

function updateGraph() {
  
	svg
    .selectAll( 'path' )
  	.remove();
  
	svg
    .selectAll( 'circle' )
  	.remove();  
  
	svg
    .selectAll( 'circle' )
    .data( data )
    .enter()
    .append( 'circle' )
      .attr( 'cx', function( d ) { return d.x; })
      .attr( 'cy', function( d ) { return d.y; })
      .attr( 'r', 2 )
      .attr( 'stroke', 'none' )
      .attr( 'fill', 'red' );  

  var contours = svg
    .selectAll( 'path' )
    .data( d3.contourDensity()
      .x( function( d ) { return d.x; } )
      .y( function( d ) { return d.y; } )
      .size( [width, height] )
      .bandwidth( 70 )
      ( data )
  );
  
  contours
    .enter()
   	.append( "path" )
    .attr( "d", d3.geoPath() );
}

updateData();
updateGraph();

setInterval( function() {
	updateData();
  updateGraph();
}, 2000 );
&#13;
<script src="https://d3js.org/d3-contour.v1.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="800" height="600" fill="none" stroke="blue" stroke-linejoin="round"></svg>
&#13;
&#13;
&#13;

更新

我试图实现的是缓慢变化的等高线图(如天气图)的视觉效果,因此基础数据并不重要。

我的第一次尝试是用噪点填充点阵列并使用d3.contours()绘制轮廓。通过在每个动画刻度上更改噪声的种子,可以对等高线图进行动画处理,但它非常耗费资源。轮廓也不平滑,我没有设法控制图案的比例和细节。

这就是为什么我切换到基于少量数据构建平滑轮廓的d3.contourDensity()的原因。

我是否错过了创建等高线图连续动画的正确方法?

我与d3.contourDensity()最接近的是以下内容。但正如@ gerardo-furtado所指出的那样,路径的转换意味着将更新之间的路径联系起来,这似乎是不可能的:

&#13;
&#13;
var data = [];

var svg = d3.select( 'svg' );
var width = parseInt( svg.attr( 'width' ) );
var height = parseInt( svg.attr( 'height' ) );

var speed = 4;

function updateData() {
	for( var i = 0; i < 50; i++ ) {
  	if( !data[i] ) { 
	    data[i] = {
  	    x: Math.random() * 800,
    	  y: Math.random() * 600
    	}
    } else {
    	data[i].x = data[i].x + ( ( 0.5 - Math.random() ) * 2 * speed );
    	data[i].y = data[i].y + ( ( 0.5 - Math.random() ) * 2 * speed );      
    }
  }
}

function updateGraph() {
  
	svg
    .selectAll( 'path' )
  	.remove();
  
	svg
    .selectAll( 'circle' )
  	.remove();  
  
	svg
    .selectAll( 'circle' )
    .data( data )
    .enter()
    .append( 'circle' )
      .attr( 'cx', function( d ) { return d.x; })
      .attr( 'cy', function( d ) { return d.y; })
      .attr( 'r', 2 )
      .attr( 'stroke', 'none' )
      .attr( 'fill', 'red' );  

  var contours = svg
    .selectAll( 'path' )
    .data( d3.contourDensity()
      .x( function( d ) { return d.x; } )
      .y( function( d ) { return d.y; } )
      .size( [width, height] )
      .bandwidth( 70 )
      ( data )
  );
  
  contours
    .enter()
   	.append( "path" )
    .attr( "d", d3.geoPath() );
}

updateData();
updateGraph();

setInterval( function() {
	updateData();
  updateGraph();
}, 500 );
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-contour.v1.min.js"></script>
<svg width="800" height="600" fill="none" stroke="blue" stroke-linejoin="round"></svg>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:2)

您可以使用一组非常基本的输入,更新和退出选项来转换路径元素,这不是问题。

真正的问题是:每次运行updateGraph函数时,轮廓生成器都会创建不同数量的路径元素。所以,问题是:

  1. 您将如何设置输入选择的动画?我的意思是,他们来自哪里?从SVG的中心,从SVG的一个角落,还是从其他地方出发?
  2. 您将如何为退出选择设置动画?他们会去哪里?
  3. 最重要的是:您将如何设置更新选择的动画?围绕数据点的给定路径如何与另一个数据点周围的另一条路径相关?您是要通过索引(默认行为)关联它们,还是要创建关键功能?如果是,您将使用什么属性d.value
  4. 正如您所看到的,问题比看起来更复杂。例如,这是您的代码,具有基本的输入,退出和更新选择。我设置了更新选择的动画,并使用0不透明度淡入/淡出输入和退出选择。结果不愉快:

    &#13;
    &#13;
    var data = [];
    
    var svg = d3.select('svg');
    var width = parseInt(svg.attr('width'));
    var height = parseInt(svg.attr('height'));
    
    function updateData() {
      for (var i = 0; i < 50; i++) {
        data[i] = {
          x: Math.random() * 800,
          y: Math.random() * 600
        }
      }
    }
    
    function updateGraph() {
    
      var circles = svg
        .selectAll('circle')
        .data(data);
    
      circles.enter()
        .append('circle')
        .attr('cx', function(d) {
          return d.x;
        })
        .attr('cy', function(d) {
          return d.y;
        })
        .attr('r', 2)
        .attr('stroke', 'none')
        .attr('fill', 'red');
    
      circles.transition()
        .duration(1000)
        .attr('cx', function(d) {
          return d.x;
        })
        .attr('cy', function(d) {
          return d.y;
        });
    
      var contours = svg
        .selectAll('path')
        .data(d3.contourDensity()
          .x(function(d) {
            return d.x;
          })
          .y(function(d) {
            return d.y;
          })
          .size([width, height])
          .bandwidth(70)
          (data)
        );
    
      contours.exit().transition()
      	.duration(1000)
        .style("opacity", 0)
        .remove();
    
      contours.enter()
        .append("path")
        .style("opacity", 0)
        .attr("d", d3.geoPath())
        .transition()
        .duration(1000)
        .style("opacity", 1);
    
      contours.transition()
        .duration(1500)
        .attr("d", d3.geoPath());
    }
    
    updateData();
    updateGraph();
    
    setInterval(function() {
      updateData();
      updateGraph();
    }, 2000);
    &#13;
    <script src="https://d3js.org/d3-contour.v1.min.js"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="800" height="600" fill="none" stroke="blue" stroke-linejoin="round"></svg>
    &#13;
    &#13;
    &#13;

答案 1 :(得分:0)

我不会尝试对轮廓本身进行动画处理,这很麻烦。而是尝试插值基础点数据,例如使用ICP,或作为一次廉价尝试,在时间t的集合中的每个点与时间步t + 1的集合中的最近点之间计算1:1关联(尽管每个点必须只有一个对应的通讯者,贪婪的方法才足够)。如果数据的时间采样过于粗糙,以至于无法使用静态密度轮廓线进行平滑动画处理,则对密度场连续变化进行插值通常是非常好的近似值。从点集的每个插值状态静态提取密度等值线并将其绘制成连续序列应可得到预期的结果。

答案 2 :(得分:0)

通过跟踪进入和退出的点,然后补间应用于每个点的weight,可以过渡轮廓。

Demo

进入点将逐渐增加其对轮廓生成器的影响,而退出点将逐渐减小其影响,从而实现平滑过渡。

为保持轮廓一致,有助于指定threshold值的显式数组。