简体   繁体   English

D3 中是否有“轴相等”?

[英]Is there an "axis equal" in D3?

I am using D3 and Python to create some plots.我正在使用 D3 和 Python 创建一些图。 With Python I am able to do axis equal, as shown in image 1:使用 Python 我可以使轴相等,如图 1 所示:

在 Python 中使用 Axis Equal

I want to achieve the same in an SVG using D3.我想使用 D3 在 SVG 中实现相同的效果。 Please refer to the image 2, without axis equal:请参考图 2,没有轴相等:

Javascript d3中没有轴等于

The axes scale which I am using are as follows:我使用的轴比例如下:

var xScale = d3.scaleLinear()
    .range([0, width])
    .domain([minX, maxX]);

var yScale = d3.scaleLinear()
    .range([height, 0])
    .domain([minY, maxY]);

var xAxis = d3.axisBottom(xScale).ticks(5),
    yAxis = d3.axisLeft(yScale).ticks(5);

Could anyone tell me how can I achieve the axis equal with D3?谁能告诉我如何实现与 D3 相等的轴?

Short answer : no, there is no "axis equal" in D3.简短回答:不,D3 中没有“轴相等” D3 is quite low level, it's just a collection of methods, so you have to do almost everything by hand ... D3是相当低级的,它只是方法的集合,所以你几乎所有的事情都必须手工完成......

The good news is that all you need for solving your problem is some math to calculate the new ranges.好消息是,解决问题所需的只是一些数学运算来计算新的范围。

As an introduction, since this is a tagged question, "axis equal" is a term used in some programs like Matlab , which...作为介绍,由于这是一个标记的问题,因此“轴相等”是一些程序中使用的术语,例如Matlab ,它...

Use the same length for the data units along each axis.对沿每个轴的数据单位使用相同的长度。

It can be better explained visually.在视觉上可以更好地解释它。 Have a look at this image, with different domains and ranges ( source ):看看这张图片,有不同的域和范围( 来源):

在此处输入图片说明

After this brief introduction, let's go back to your problem.在这个简短的介绍之后,让我们回到你的问题。 Suppose this simple code, generating two axes:假设这个简单的代码,生成两个轴:

 const minX = 0, minY = 0, maxX = 120, maxY = 50, width = 500, height = 300, paddings = [10, 10, 20, 30]; const xScale = d3.scaleLinear() .range([paddings[3], width - paddings[1]]) .domain([minX, maxX]); const yScale = d3.scaleLinear() .range([height - paddings[2], paddings[0]]) .domain([minY, maxY]); const svg = d3.select("svg") .attr("width", width) .attr("height", height); svg.append("g") .attr("transform", `translate(0,${height - paddings[2]})`) .call(d3.axisBottom(xScale).tickSizeInner(-height + paddings[2] + paddings[0])); svg.append("g") .attr("transform", `translate(${paddings[3]},0)`) .call(d3.axisLeft(yScale).tickValues(xScale.ticks()).tickSizeInner(-width + paddings[3] + paddings[1]));
 svg { border: 2px solid tan; } line { stroke-dasharray: 2, 2; stroke: gray; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg></svg>

As we can clearly see, the units are not equally spaced.正如我们可以清楚地看到的,这些单位不是等距的。 We can do two things for creating axes with equally spaced units: 1. Change the domains;我们可以做两件事来创建具有等距单位的轴:1. 更改域; 2. Change the ranges. 2. 更改范围。

According to your comment , changing the domains is not an option.根据您的评论,更改域不是一种选择。 So, let's change the ranges instead.因此,让我们改为更改范围。

All we need is calculating the number of units per user space, and using the biggest among them (x or y) to change the range of the opposite scale.我们所需要的只是计算每个用户空间的单位数,并使用其中最大的(x 或 y)来改变相反比例的范围。

For instance, given the scales of the snippet above:例如,给定上面代码片段的比例:

const xStep = Math.abs((xScale.domain()[1] - xScale.domain()[0]) / (xScale.range()[1] - xScale.range()[0]));

const yStep = Math.abs((yScale.domain()[1] - yScale.domain()[0]) / (yScale.range()[1] - yScale.range()[0]));

xStep is bigger than yStep , showing us that the x axis has more units per pixels. xStep大于yStep ,向我们展示了 x 轴每个像素有更多的单位。 Therefore, we'll change the y axis range:因此,我们将更改 y 轴范围:

yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);

And here is the demo:这是演示:

 const minX = 0, minY = 0, maxX = 120, maxY = 50, width = 500, height = 300, paddings = [10, 10, 20, 30]; const xScale = d3.scaleLinear() .range([paddings[3], width - paddings[1]]) .domain([minX, maxX]); const yScale = d3.scaleLinear() .range([height - paddings[2], paddings[0]]) .domain([minY, maxY]); const xStep = Math.abs((xScale.domain()[1] - xScale.domain()[0]) / (xScale.range()[1] - xScale.range()[0])); const yStep = Math.abs((yScale.domain()[1] - yScale.domain()[0]) / (yScale.range()[1] - yScale.range()[0])); yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]); const svg = d3.select("svg") .attr("width", width) .attr("height", height); svg.append("g") .attr("transform", `translate(0,${height - paddings[2]})`) .call(d3.axisBottom(xScale).tickSizeInner(-height + paddings[2] + yScale.range()[1])); svg.append("g") .attr("transform", `translate(${paddings[3]},0)`) .call(d3.axisLeft(yScale).tickValues(yScale.ticks().filter(function(d){return !(+d%10)})).tickSizeInner(-width + paddings[3] + paddings[1]));
 svg { border: 2px solid tan; } line { stroke-dasharray: 2, 2; stroke: gray; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg></svg>

As you can see using the gridlines, the units are equally spaced now.正如您使用网格线所看到的,单位现在等距。

Finally, have in mind that determining programmatically what scale will be changed, readjusting the correct range (your y scale, for instance, goes from the bottom to the top), centralising the axes etc are whole different issues.最后,请记住,以编程方式确定将更改的比例、重新调整正确的范围(例如,您的 y 比例从底部到顶部)、将轴居中等是完全不同的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM