question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Lookup Table's Lut.getColor() should use Math.floor instead of Math.round

See original GitHub issue

Describe 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:

https://github.com/mrdoob/three.js/blob/cd4f4e7fac6faeaf79bc53fcc64ccc3e88aaa872/examples/jsm/math/Lut.js#L108

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:

imagen

Platform:

  • Device: Desktop
  • OS: Linux
  • Browser: Firefox
  • Three.js version: r137

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:7

github_iconTop GitHub Comments

2reactions
scurestcommented, Feb 22, 2022

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).

A

(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).

B

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.

2reactions
scurestcommented, Feb 22, 2022

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

> a = 1/6
> a+a+a+a+a+a
< 0.9999999999999999
> a = 1/5
> a+a+a+a+a
< 1.0

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.

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found