我有一个colorscaleMap让我们假设这样
d3.scale.linear()
.domain([0.01,0.02,0.03,0.04,0.05])
.range(["#5100ff", "#00f1ff", "#00ff30", "#fcff00", "#ff0000"])
现在我要使用反转功能
让我们说invert(“#5100ff”),我期望输出为0.01,但是invert功能不适用于非数字值。如https://github.com/d3/d3-scale中所述 仅当范围为数字时才支持此方法。如果范围不是数字,则返回NaN
问题: 这与具有超过10000个数据点的大数据频谱图有关。 Y-轴是频率,X轴是时间,颜色表示振幅。我认为编写Index函数效率很低
有人可以解决这个问题吗?
答案 0 :(得分:1)
这不是一件容易的事,如果可以的话,最好通过使用绑定数据来避免此问题,这可能是最好的。
但是,这是一个非常有趣的问题。反转数字时,至少要绑定到一个维度,数字将属于可插值的范围(某些特定例外)。有了三维色彩空间,这会变得有点困难,尤其是对于分段的域和范围。但是,即使我们将无法提供颜色的颜色提供给刻度尺无法产生的反转功能,也仍然很困难。
如前所述,d3-scale不支持非数值反演,d3-interpolate也不提供反演支持。我们也不能连续地拥有非数值域(这将提供最简单的解决方案)。无需重写每个颜色插值器,任何解决方案都可能特定于某个插值(默认是每个通道上的线性插值,所以我将在下面使用它)。
如果我们能够解决上述问题,那么最后的挑战就是您将无法拥有很多精度:RGB通道值被舍入为0到255之间的8位数字。将这个数字取反会产生舍入误差。
此舍入问题通常是由于许多色标在任何给定通道上未使用0-255的整个范围这一事实而加剧的。如果从红色变为橙色,则变化最大的通道是绿色,变化为165。这肯定会降低任何反相函数的精度。
也就是说,我们可以为具有一定色彩空间范围的标尺制作简单的求反函数。这可能需要一些步骤:
a
,b
)之间具有最大范围的颜色通道。y
。y
和a
之间解插b
,给我们t
t
并在音阶域的最小值和最大值之间进行插值线性插值器看起来像:y = a + t * (b - a)
这样,我们就可以进行插值:t = (y - a)/(b - a)
t
是介于0和1之间的数字,其中(当应用于插值器时)0将在域中产生最小值,而1将在域中产生最大值。
这是它的外观:
var scale = d3.scaleLinear()
.domain([0,100])
.range(["orange","red"])
scale.invertColor = function(x) {
// get the color into a common form
var color = d3.color(x);
// do the same for the range:
var min = d3.color(this.range()[0]);
var max = d3.color(this.range()[1]);
// find out which channel offerrs the greatest spread between min and max:
var spreads = [max.r-min.r,max.g-min.g,max.b-min.g].map(function(d) { return Math.abs(d); });
var i = spreads.indexOf(Math.max(...spreads));
var channel = d3.keys(color)[i];
// The deinterpolation function
var deinterpolate = function (value,a,b) {
if(b-a==0) return 0;
return Math.abs((value - a) / (b - a))
}
// Get t
var t = deinterpolate(color[channel],min[channel],max[channel]);
// Interpolate between domain values with t to get the inverted value:
return d3.interpolate(...this.domain())(t)
};
var test = [0,10,50,90,100];
console.log("Test values:");
console.log(test)
console.log("Scaling and then Inverting values:");
console.log(test.map(function(d) { return scale.invertColor(scale(d)); }));
console.log("Original values minus scaled and inverted values:");
console.log(test.map(function(d) { return (d - scale.invertColor(scale(d))); }));
<script src="https://d3js.org/d3.v5.min.js"></script>
上面有限制,您可以输入任何颜色。即使该标尺无法产生该值,它也会为您插值。
通过多次使用上述功能,很容易针对多个细分进行修改。我们可以检查每个分段,以查看是否每个分段的值一次取反后会返回其自身。这样做只能将刻度尺产生的颜色反转:
var scale = d3.scaleLinear()
.domain([0,100,200])
.range(["orange","red","purple"])
scale.invertColor = function(x) {
var domain = this.domain();
var range = this.range();
// check each segment to see if the inverted value scales back to the original value:
for(var i = 0; i < domain.length - 1; i++) {
var d = [domain[i], domain[i+1]];
var r = [range[i], range[i+1]];
var inverted = (invert(x,d,r))
if (d3.color(this(inverted)).toString() == d3.color(x).toString()) return inverted;
}
return NaN; // if nothing matched
function invert(x,d,r) {
// get the color into a common form
var color = d3.color(x);
// do the same for the range:
var min = d3.color(r[0]);
var max = d3.color(r[1]);
// find out which channel offerrs the greatest spread between min and max:
var spreads = [max.r-min.r,max.g-min.g,max.b-min.g].map(function(d) { return Math.abs(d); });
var i = spreads.indexOf(Math.max(...spreads));
var channel = d3.keys(color)[i];
// The deinterpolation function
var deinterpolate = function (value,a,b) {
if(b-a==0) return 0;
return Math.abs((value - a) / (b - a))
}
// Get t
var t = deinterpolate(color[channel],min[channel],max[channel]);
// Interpolate between domain values with t to get the inverted value:
return d3.interpolate(...d)(t)
}
};
var test = [0,10,50,90,100,110,150,190,200];
console.log("Test values:");
console.log(test)
console.log("Scaling and then Inverting values:");
console.log(test.map(function(d) { return scale.invertColor(scale(d)); }));
console.log("Original values minus scaled and inverted values:");
console.log(test.map(function(d) { return (d - scale.invertColor(scale(d))); }));
<script src="https://d3js.org/d3.v5.min.js"></script>