Lookup Table's Lut.getColor() should use Math.floor instead of Math.round
See original GitHub issueDescribe the bug
In Lut.getColor(), once the input value is converted into an alpha between 0 and 1, alpha is multiplied by the number of colors to assign it an index, and then rounded to the nearest integer using Math.round
.
I think this might be wrong, as alpha should be rounded to the nearest integer below, using Math.floor
.
Otherwise, values that fall under a color i but are closer to its border with the color i+1 will eventually be assigned the color i+1.
Here is the referred line in the source code:
To Reproduce
See code snippet below.
Code
import { Lut, ColorMapKeywords } from 'three/examples/jsm/math/Lut';
const colormap = ColorMapKeywords.rainbow;
const lut = Lut( colormap, 6 );
lut.setMax( 100.0 );
lut.setMin( 0.0 );
// This value gets a correct color
// alpha = 0.7
// alpha * nrColors = 4.2
// getColor picks this.lut[4]
const a = 70;
const colorA = lut.getColor( a );
// This value gets a wrong color
// alpha = 0.6
// alpha * nrColors = 3.6
// getColor picks this.lut[4] instead of this.lut[3]
const b = 60;
const colorB = lut.getColor( b );
Screenshots
Maybe this schematic helps to illustrate my point:
Platform:
- Device: Desktop
- OS: Linux
- Browser: Firefox
- Three.js version: r137
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:7
Top Results From Across the Web
Math.floor() - JavaScript - MDN Web Docs
The Math.floor() function always rounds down and returns the largest integer less than or equal to a given number.
Read more >Why is Math.floor() preferred over Math.round()? [JavaScript]
Summary: Because with Math.round() the values in min and max are underrepresented. let's have an example and compare the results when using ...
Read more >LookupTable – three.js docs
Lut. Represents a lookup table for colormaps. It is used to determine the color values from a range of data values. Code Example....
Read more >Why Math.floor(Math.random() * 2) instead of Math.round()?
floor() takes any value and rounds to the nearest integer not greater than the value. The value it takes can be any number,...
Read more >How Look Up Tables (LUTs) make your code Smaller, Faster ...
comWebsite ➤ https://www.jacobsorber.com---How Lookup Tables make your ... It's a data structure — really just an array used a certain way.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Thinking more about it:
There are two obvious ways to do this. One is to divide the interval into n equal pieces and store the sample at the midpoint of each piece in lut[k]. In this case you would use the floor just like OP said,
floor(n*alpha)
. You would also use lut[n-1] instead of lut[n] (which the current code does).(Pink dots are the sample locations, gray crosses are where it switches from one bucket to the next).
The other way is to sample at the points 0, 1/n, 2/n, …, (n-1)/n, 1. This creates n+1 segments (note the first and last are half the size of the others).
The current code is sampling at these points, right? So it should be doing this. Since the lut[k] bucket holds the color sampled at the point k/n, you would want to use the k bucket when alpha is closer to k/n than to (k-1)/n or (k+1)/n. That’s the range [k/n - 1/2n, k/n + 1/2n), or multiplying by n, the range [k - 1/2, k + 1/2). This makes round correct.
However if this is the intent of the current code, there are two problems. There should always be n+1 buckets in lut, and the lut[n] bucket should be used (it should not use lut[n-1] instead).
AFAICT combined these mean the only visible problem is when sampling near alpha=1, sometimes the color at 1-1/n is returned when the color at 1 should be.
https://github.com/mrdoob/three.js/blob/f6e6fa192a4a0f657cfa45be464e9b077b7bdab3/examples/js/math/Lut.js#L46-L60
For some n, i will be exactly 1 at the end of the loop, but some will only be close to 1
If i >= 1 at the end, then the second condition in the if is never true, so an element isn’t pushed to lut on the last i=1.0 iteration. But if i<1 at the end, the last iteration does push an element.
That’s why lut is only sometimes different by 1.