Tried to draw a sinusoid with positive and negative parts in different color using path
element and fill
stroke but got a weird/confusing output.
Here's the D3 code:
window.onload = function(){
let xy_values =[];
let xy_values2 =[];
let len = 99; //Number of points
let dt = 8*Math.PI/len; //X-Distance between points
for (var i =0; i<len; i ){
let valx = i*dt;
let valy = Math.sin(valx);
if(valy > 0){
// Positive sine
xy_values.push( {key: valx, value: valy} );
xy_values2.push( {key: valx, value: 0.0} );
}else{
//Negative sine
xy_values.push( {key: valx, value: 0.0} );
xy_values2.push( {key: valx, value: valy} );
}
}
draw(xy_values, xy_values2);
}
function draw(cdata, ddata){
var height=200;
var width=800;
let svg=d3.select("#container").append("svg").attr("width", width).attr("height", height);
walkX = d3.scaleLinear()
.domain([0, 30])
.range([40, width ])
walkY = d3.scaleLinear()
.domain([-1, 1])
.range([height-10, 10])
// Add the green regions
svg.append("path")
.datum(cdata)
.attr("fill", "#BFB")
.attr("stroke", "none")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function(d) { return walkX(d.key) })
.y(function(d) { return walkY(d.value) })
);
// Add the red regions
svg.append("path")
.datum(ddata)
.attr("fill", "#FBB")
.attr("stroke", "none")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function(d) { return walkX(d.key) })
.y(function(d) { return walkY(d.value) })
);
// Add scatter points
svg.append('g')
.selectAll("dot")
.data(ddata)
.enter()
.append("circle")
.attr("cx", function (d) { return walkX(d.key); } )
.attr("cy", function (d) { return walkY(d.value); } )
.attr("r", 1.5)
.style("fill", "#69b3a2")
svg.append("g")
.attr("transform", "translate(" 0 "," 10 ")")
.call(d3.axisBottom(walkX));
svg.append("g")
.attr("transform", "translate(" 0 "," (height-10) ")")
.call(d3.axisBottom(walkX));
svg.append("g")
.attr("transform", "translate(" 0 "," (height/2) ")")
.call(d3.axisBottom(walkX));
svg.append("g")
.attr("transform", "translate(" 35 "," 0 ")")
.call(d3.axisLeft(walkY));
}
Questions
What's causing that incorrect filling/ Tilted filling ?
Is this because of some precision issue ? or is it a glitch in the way fill
is implemented in D3 ?
CodePudding user response:
Is this because of some precision issue ? or is it a glitch in the way fill is implemented in D3 ?
No, D3 is only used to create your SVG elements in the DOM not render them, the issue is how SVG fills are applied. When applying a fill to a path element, the fill connects the last point of the path with the first, which is why you see that effect. You'll see that the effect you are trying to remove forms line between these two points. The browser in rendering the SVG does not have any instruction to give the fill a different shape.
One option to fix this is to use an area generator rather than a line generator which allows the option to set a baseline.
The D3 area generator creates a path with two y values for every x value (specifying an area to be filled, rather than a path), in this case one at a baseline (y0) and one at a data value (y1):
d3.area()
.x(function(d) { return walkX(d.key) })
.y1(function(d) { return walkY(d.value) })
.y0(function() { return walkY(0) })
window.onload = function(){
let xy_values =[];
let xy_values2 =[];
let len = 99; //Number of points
let dt = 8*Math.PI/len; //X-Distance between points
for (var i =0; i<len; i ){
let valx = i*dt;
let valy = Math.sin(valx);
if(valy > 0){
// Positive sine
xy_values.push( {key: valx, value: valy} );
xy_values2.push( {key: valx, value: 0.0} );
}else{
//Negative sine
xy_values.push( {key: valx, value: 0.0} );
xy_values2.push( {key: valx, value: valy} );
}
}
draw(xy_values, xy_values2);
}
function draw(cdata, ddata){
var height=200;
var width=800;
let svg=d3.select("#container").append("svg").attr("width", width).attr("height", height);
walkX = d3.scaleLinear()
.domain([0, 30])
.range([40, width ])
walkY = d3.scaleLinear()
.domain([-1, 1])
.range([height-10, 10])
// Add the green regions
svg.append("path")
.datum(cdata)
.attr("fill", "#BFB")
.attr("stroke", "none")
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(function(d) { return walkX(d.key) })
.y1(function(d) { return walkY(d.value) })
.y0(function() { return walkY(0) })
);
// Add the red regions
svg.append("path")
.datum(ddata)
.attr("fill", "#FBB")
.attr("stroke", "none")
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(function(d) { return walkX(d.key) })
.y1(function(d) { return walkY(d.value) })
.y0(function() { return walkY(0) })
);
// Add scatter points
svg.append('g')
.selectAll("dot")
.data(ddata)
.enter()
.append("circle")
.attr("cx", function (d) { return walkX(d.key); } )
.attr("cy", function (d) { return walkY(d.value); } )
.attr("r", 1.5)
.style("fill", "#69b3a2")
svg.append("g")
.attr("transform", "translate(" 0 "," 10 ")")
.call(d3.axisBottom(walkX));
svg.append("g")
.attr("transform", "translate(" 0 "," (height-10) ")")
.call(d3.axisBottom(walkX));
svg.append("g")
.attr("transform", "translate(" 0 "," (height/2) ")")
.call(d3.axisBottom(walkX));
svg.append("g")
.attr("transform", "translate(" 35 "," 0 ")")
.call(d3.axisLeft(walkY));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.1.0/d3.min.js"></script>
<div id="container"></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Which gives us: