2013-06-09 3 views
1

Я пытаюсь получить график компоновки сил (например, http://bl.ocks.org/mbostock/1748247) в D3, чтобы играть хорошо с реактивным источником данных (meteor.js). Я относительно новичок в обоих этих мирах, и я сталкиваюсь с этой проблемой. У меня правильная компоновка сил и настройка источника данных, так как моя коллекция метеоров отлично работает, но когда я обновляю или добавляю в базу данных с консоли, все узлы летают, как будто только что сгенерированы. Это похоже на то, что все данные рассматриваются как новые, а не только добавление новых данных или переход текущих узлов в соответствие с обновлением.График компоновки диаграмм перерисовывается в D3 при обновлении meteor db

Я пробежал все, что мог, что обсуждал D3 и метеорит, но мое понимание понятий здесь недостаточно сильное, чтобы получить дальнейшее развитие. Оцените любую помощь или точку в правильном направлении.

Мой шаблон для области d3 заворачивают в #constant так:

<template name="ideaspace"> 
{{#constant}} 
<svg> 
</svg> 
{{/constant}} 
</template> 

Тогда на стороне клиента, я получил это. (Простите за ужасный код я очень далеко вниз по кроличьей целом здесь, просто пытаясь все я сталкиваюсь, чтобы увидеть, если я могу получить некоторые подсказки.)

Template.ideaspace.rendered = function() { 
var self = this; 
self.node = self.find("svg"); 

if(! self.handle) { 
    self.handle = Deps.autorun(function() { 
//d3 code 
var nodes = DataPoints.find().fetch(); 
var force = d3.layout.force() 
     .nodes(nodes) 
     .size([width, height]) 
     .gravity(.02) 
     .charge(0) 
     .on("tick", tick) 
     ; 
    var svg = d3.select("svg") 
     .attr("width", width) 
     .attr("height", '100%') 
     .attr("id", 'container') 
     .style("top", '30px') 
     .style("position", 'fixed') 
     .attr("pointer-events", "all") 

    svg.append("rect") 
     .attr("width", "100%") 
     .attr("height", "100%") 
     .attr("fill", "whitesmoke") 
     .call(d3.behavior.zoom() 
      .on("zoom", function() { 
      scale = d3.event.scale; 
      if (scale<=1){ 
       width = $(window).width()/d3.event.scale; 
       foci = [{x: '0%', y: 150}, {x: width*1/4, y: 1/2*height}, {x: width*2/4, y: 1/2*height}, {x: width*3/4, y: 1/2*height}]; 
      }       
      svg.attr("transform", "translate(" + d3.event.translate + 
       ")scale(" + d3.event.scale + ")"); 
      }));  

    var x = 
    d3.scale.linear() 
     .domain([0, width]) 
     .range([0, width]); 

    var y = 
    d3.scale.linear() 
     .domain([0, height]) 
     .range([0, height]); 

    var bubble = svg.selectAll(".bubble"); 

    bubble = bubble.data(nodes,function (party) { return party._id; }); 
    width = $(window).width(); 

    bubble.enter().append("svg:circle") 
     .style("fill", function(d) { return color(+d.emperical); }) 
     .style("stroke", "grey") 
     .attr("id", function(d) { return d.objectId+"_c"; }) 
     .attr("r", function(d) { return d.radius; }) 
     .call(force.drag); 

    bubble.transition().duration(5000); 
    force.start(); 

    function tick(e) { 
     bubble 
     .each(collide(.5)) 
     .attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")"; 
     })    
    } 
function cluster(alpha) { 
     var max = {}; 

     // Find the largest node for each cluster. 
     nodes.forEach(function(d) { 
     if (!(d.emperical in max) || (d.radius > max[d.emperical].radius)) { 
      max[d.emperical] = d; 
     } 
     }); 

     return function(d) { 
     var node = max[d.emperical], 
      l, 
      r, 
      x, 
      y, 
      i = -1; 

     if (node == d) return; 

     x = d.x - node.x; 
     y = d.y - node.y; 
     l = Math.sqrt(x * x + y * y); 
     r = d.radius + node.radius; 
     if (l != r) { 
      l = (l - r)/l * alpha; 
      d.x -= x *= l; 
      d.y -= y *= l; 
      node.x += x; 
      node.y += y; 
     } 
     }; 
    } 
    }) 
}; 

};

UPDATE: Это привело к добавлению новых данных. Теперь, я думаю, мне нужно что-то построить, чтобы проверить, были ли изменены какие-либо другие поля.

 self.handle = Deps.autorun(function() {  

     var topicPoints = TopicPoints.find().fetch(); 
     var found = true; 
     topicPoints.forEach(function (d) {    
      if(nodes.length>0){        
      for(var i = 0; i < nodes.length; i++) { 
       if (nodes[i]._id == d._id) { 
        console.log("Found "+ d._id) 
        found = true; 
        break; 
       } else { 
        found = false 
        console.log("Not Found "+ d._id) 
       } 
      } 
      if(!found){      
       nodes.push({ 
       _id:d._id, 
       title: d.title, 
       radius: d.radius 
       });  
      } 

      } else { 
       nodes = topicPoints; 
      } 


     }); 

     render(nodes); 

    }) 

ответ

3

Проблема заключается в том, что вы построение nodes массива свежего с Datapoints.find().fetch(). Этот массив (возможно) отсутствуют следующие атрибуты:

  • индекс - индекс отсчитывается от нуля узла в массиве узлов в.
  • x - координата x текущего положения узла.
  • y - координата y текущего положения узла.
  • px - координата x предыдущего положения узла.
  • py - y-координата предыдущей позиции узла.
  • fixed - логическое значение, указывающее, заблокировано ли положение узла.
  • вес - вес узла; количество связанных ссылок.

Эти атрибуты не являются строго необходимы для вызова force.nodes(), но если они не присутствуют в массиве, то они будут случайно инициализируется force.start() на первый вызов. Это то, что вы наблюдаете.

Следовательно, решение состоит в том, чтобы повторно использовать один массив nodes, который сохраняется по звонкам rendered. Когда вам нужно обновить данные, объедините массив с новыми данными самостоятельно (возможно, используя набор party._id или умный запрос БД вместо .find().fetch()). Это сохранит старые объекты, у которых уже установлены указанные выше атрибуты, и вставьте новые элементы, которые не имеют этих атрибутов. Затем новые входящие объекты будут случайно размещены и ассимилированы после вызова force.start().

Это также позволяет вам иметь большую гибкость в том, как вставлять обновления. Например, если вы хотите, чтобы новые объекты всегда приходили из левого угла, во время слияния вы можете установить x и y в 0 для новых объектов.

+0

Благодарим за быстрый ответ - это заставило меня двигаться в правильном направлении. Однако мое решение кажется очень неэффективным. Было бы лучше сделать это объектом? Любые идеи о том, как более эффективно определять, есть ли новые объекты, добавленные в массив, или если какая-либо из данных изменилась? –