Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
185 views
in Technique[技术] by (71.8m points)

javascript - d3.js circle plotting does not working properly while trying to load after first time

This is my d3.js piece of code :

<script>


var width = 300,
height = 300,
margin = 20;
var x_centre = width/2;
var y_centre = height/2;
var nuclear_radius = 15;

var vis = d3.select("#chart_container").append("svg:svg")
.attr("width", width)
.attr("height", height);

var radiuses = [1,2,3];

var multiplier = (d3.min([width, height]) - 2*margin - nuclear_radius) / (d3.max(radiuses) * 2);

var shells = vis.selectAll("circle.shell")
 .data(radiuses)
 .enter().append("circle")
 .attr("class", "shell")
 .attr("cx", x_centre)
 .attr("cy", y_centre)
 .attr("r", function(d, i) { 
     return (d * multiplier);
  });

var nucleus = vis.selectAll('circle.nucleus')
.data(['nucleus'])
.enter().append("circle")
.attr("class", "nucleus")
.attr("cx", x_centre)
.attr("cy", y_centre)
.attr("r", nuclear_radius);


function  showGraph(de)  {
var electrons = de


var radius_counts = {};
for (var i = 0; i < electrons.length; i++) { 
if (electrons[i].distance in radius_counts) { 
     radius_counts[electrons[i].distance] += 1;
} else { 
     radius_counts[electrons[i].distance] = 1;
} 
}

// Calculate the x- and y-coordinate values 
// for each electron.
var keep_count = {};
electrons.forEach(function(d) { 
var siblings = radius_counts[d.distance];
if (d.distance in keep_count) { 
  keep_count[d.distance] += 1;    
} else { 
    keep_count[d.distance] = 1;
}
var angle = (360/siblings) * keep_count[d.distance];
var hyp = d.distance * multiplier;  
if (angle <= 90) {
  var use_angle = (90 - angle) * (Math.PI/180);
  var opp = Math.sin(use_angle) * hyp;
  var adj = Math.cos(use_angle) * hyp;
    d.x = x_centre + adj;
    d.y = y_centre - opp;
}    else if (angle <= 180) { 
        var use_angle = (180 - angle) * (Math.PI/180);
      var opp = Math.sin(use_angle) * hyp;
      var adj = Math.cos(use_angle) * hyp;
        d.x = x_centre + opp;
        d.y = y_centre + adj;    
    } else if (angle <= 270) { 
    var use_angle = (270 - angle) * (Math.PI/180);
  var opp = Math.sin(use_angle) * hyp;
  var adj = Math.cos(use_angle) * hyp;
    d.x = x_centre - adj;
    d.y = y_centre + opp;    
} else { 
    var use_angle = (360 - angle) * (Math.PI/180);    
  var opp = Math.sin(use_angle) * hyp;
  var adj = Math.cos(use_angle) * hyp;
    d.x = x_centre - opp;
    d.y = y_centre - adj;
}

});

var electron_nodes = vis.selectAll('circle.electron')
.data(electrons)
.enter().append("circle")
.attr("class", "electron")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 7)
.on("mouseover", function(d) { 
   // how to drag the electron around the circle?
});
var svgData = vis.selectAll("circle.electron")
    .data(electrons);   
svgData.exit().remove();       
}

</script> 

Now i am calling the above showGraph js function every time on click event which takes the count parameter

function add_py()
{
 var count = $( "#count" ).val();
    $.ajax({
        type: "get",
        url: "abc.py",
        data: {'count': count},
        datatype:"script",
        async: false,
        success: function(response) {
        var data= JSON.parse(response);
        //alert(response)
        showGraph(data);
        }, // success closed
        error:function(xhr,err)
        {
            alert("Error connecting to server, please contact system administator.");
        }
    })//ajax closed
}

Now when I give count 10 , the SVG is like this :

<svg width="300" height="300">
    <circle class="shell" cx="150" cy="150" r="40.833333333333336">
    <circle class="shell" cx="150" cy="150" r="81.66666666666667">
    <circle class="shell" cx="150" cy="150" r="122.5">
    <circle class="nucleus" cx="150" cy="150" r="15">
    <circle class="electron" cx="231.66666666666669" cy="150" r="7">
    <circle class="electron" cx="256.08811196359375" cy="211.25" r="7">
    <circle class="electron" cx="43.91188803640625" cy="211.25" r="7">
    <circle class="electron" cx="185.36270398786456" cy="170.41666666666669" r="7">
    <circle class="electron" cx="114.63729601213541" cy="170.41666666666666" r="7">
    <circle class="electron" cx="150" cy="231.66666666666669" r="7">
    <circle class="electron" cx="150" cy="27.5" r="7">
    <circle class="electron" cx="150" cy="109.16666666666666" r="7">
    <circle class="electron" cx="68.33333333333333" cy="150" r="7">
    <circle class="electron" cx="150" cy="68.33333333333333" r="7">
</svg>

Works fine exactly 10 circles with class electron with different cx and cy plots . But if i give first count 7 or anything less then 10 then 10 , the svg is like :

<svg width="300" height="300">
    <circle class="shell" cx="150" cy="150" r="40.833333333333336">
    <circle class="shell" cx="150" cy="150" r="81.66666666666667">
    <circle class="shell" cx="150" cy="150" r="122.5">
    <circle class="nucleus" cx="150" cy="150" r="15">
    <circle class="electron" cx="150" cy="68.33333333333333" r="7">
    <circle class="electron" cx="150" cy="272.5" r="7">
    <circle class="electron" cx="150" cy="27.5" r="7">
    <circle class="electron" cx="150" cy="190.83333333333334" r="7">
    <circle class="electron" cx="150" cy="109.16666666666666" r="7">
    <circle class="electron" cx="150" cy="231.66666666666669" r="7">
    <circle class="electron" cx="150" cy="27.5" r="7">
    <circle class="electron" cx="150" cy="109.16666666666666" r="7">
    <circle class="electron" cx="68.33333333333333" cy="150" r="7">
    <circle class="electron" cx="150" cy="68.33333333333333" r="7">
</svg>

Plots exactly 10 circles with class electron but the cx and cy repeats so they overlap . But it does not overlap if I give count 10 first time .It works fine if i give a big count then small. But not if small then big. Thanks in advance for helping me.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem is you are never updating existing "electrons" when you go from 7 to 10. In your code

var electron_nodes = vis.selectAll('circle.electron')
.data(electrons)
.enter().append("circle")
.attr("class", "electron")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 7)
.on("mouseover", function(d) { 
   // how to drag the electron around the circle?
});

everything after enter() only applies to new elements added in.

It's a common confusion; I answered another question like this earlier today, so I'm going to try to explain it really clearly here (and then everyone can just link to this explanation in future!).

Chaining methods is a great part of D3, but it can only do so much. Everyone wants to chain everything together, but you have to keep track of when your chain separates out into an enter() or exit() selection, and go back to the main selection for updating.

The general update routine will normally have four "chains" (each ended with a ;):

  • A Select Chain, that selects the elements and updates the data and saves the selection in a variable, for example you could call it var elements

  • An Enter Chain, starting with elements.enter(). (or whatever variable name you used to save the Select Chain), appending the new elements and setting any attributes or styles that are going to be constant (i.e., won't change on update)

  • An Exit Chain, starting with elements.exit()., with transitions (if using) and remove()

  • An Update Chain, starting just with your saved selection variable, followed by all the methods to set the attributes or styles that need to be updated; this will also set these attributes for the first time on the elements you just created with .enter()

Yes, you can do things differently, but only if you clearly understand what is happening and why you are changing from this pattern.

This approach also avoids the repetition in your code, where you re-select your electrons and re-apply the data to get at the exit() selection.

So for your program, the application of the 4-chain update method would look like this:

//chain 1: select
var electron_nodes = vis.selectAll('circle.electron')
      .data(electrons); 

//chain 2: enter
electron_nodes.enter().append("circle")
  .attr("class", "electron")
  .on("mouseover", function(d) { 
   // Although this function uses data at the time the event happens, 
   // the actual definition of the function never changes,
   // so you only need to attach it once when you create the node.
  })
  .attr("r", 7); 

//chain 3: exit
electron_nodes.exit().remove(); 

//chain 4: update
electron_nodes.attr("cx", function(d) { return d.x; })
  .attr("cy", function(d) { return d.y; });

Sorry it took this long for someone to answer your question; it's a rather complicated program, and probably scared people off with all the math functions. For future, you don't need to include your AJAX method (assuming you've checked that it's returning the correct values), but it would help to include an example of the data that the AJAX request returns. And of course, a working example on JSFiddle or Tributary is even better!

Best,
--ABR


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...