Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

colorLerp cycles hues linearly without considering the hue spectrum is a circular wheel #6436

Closed
1 of 17 tasks
kshach opened this issue Sep 27, 2023 · 9 comments · Fixed by #6708
Closed
1 of 17 tasks

Comments

@kshach
Copy link

kshach commented Sep 27, 2023

Increasing Access

A more accurate and commonly used colorLerp in graphics taking color theory into consideration.

Most appropriate sub-area of p5.js?

  • Accessibility
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Image
  • IO
  • Math
  • Typography
  • Utilities
  • WebGL
  • Build Process
  • Unit Testing
  • Internalization
  • Friendly Errors
  • Other (specify if possible)

Feature enhancement details

ColorLerp is a function that smoothly transitions between different colors. In the RGB color mode, it works fine by simply changing the color values. However, in color lerping or gradient situations, many modern graphic software programs use the HSL (Hue, Saturation, Lightness) or HSV (Hue, Saturation, Value) color mode because it results in more natural transitions between colors.

When I switched to using the HSL color mode for ColorLerp, I noticed that it doesn't take color theory and the concept of hue being a color wheel into consideration. This means that it doesn't account for the fact that hues wrap around in a circle, going from 355 to 05, for example, should be a short transition rather than a long one that passes through all the colors in between.

I suggest improving ColorLerp by either making it automatically convert colors to the HSL color mode for smoother transitions, or when it's used in colorMode(HSL), it should be aware of the color wheel concept. This way, if you lerp between 355 and 05 in a traditional 360-degree hue setting, it would correctly understand that it should increase the value, cycling back to zero at 360, resulting in a more intuitive and visually appealing transition.

I hope this explanation clarifies the issue.

An image for reference of the color wheel and why HSL/HSV lerping usually makes more sense

@welcome
Copy link

welcome bot commented Sep 27, 2023

Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, please make sure to fill out the inputs in the issue forms. Thank you!

@ayushanand308
Copy link

ayushanand308 commented Oct 8, 2023

@kshach ,I made some adjustments to the code snippet provided on the p5.js website

image

The result shows that the first rectangle created using colorLerp appears quite different from the original red color. Are you suggesting that we should make this transition more gradual and natural, so it smoothly transitions to a different color instead of the abrupt change we see here?

colorMode(HSL);
stroke(255);
background(51);
const from = color('hsl(355, 100%, 50%)');
const to = color('hsl(05, 100%, 50%)');
colorMode(HSL);
const interA = lerpColor(from, to, 0.33);
const interB = lerpColor(from, to, 0.66);
fill(from);
rect(10, 20, 20, 60);
fill(interA);
rect(30, 20, 20, 60);
fill(interB);
rect(50, 20, 20, 60);
fill(to);
rect(70, 20, 20, 60);
describe(
);

@kshach
Copy link
Author

kshach commented Oct 12, 2023

Yes, certainly. The transition in the example given shows how unnatural the transition is and the incorrect usage of the color wheel and color theory; Transitioning between very closely related colors, like hues of red shouldn't have to go through distant colors. I believe colorLerp should go through the nearest path while considering hue degrees is a cyclical. I even suggest taking this further than just within the usage of HSL color mode. But also have a similar solution for colorLerp in the default RGB. As is done in modern graphic softwares such as Figma.

@davepagurek
Copy link
Contributor

I wonder if in lerpColor, in HSL mode, we could convert the hues to p5.Vectors and use slerp() before converting back to an angle? https://p5js.org/reference/#/p5.Vector/slerp

@ayushanand308
Copy link

@davepagurek That is a nice way to achieve the said functionality. One other way I was thinking of using a helper function to interpolate hue in a circular manner. What path do you suggest going through?

@davepagurek
Copy link
Contributor

@ayushanand308 either works, the contents of the helper could potentially include a slerp call if you want, since that includes the math for rotation towards the closest angle.

@Bumblebee00
Copy link
Contributor

hello everyone @davepagurek, @ayushanand308, @kshach, I am new to contributing to p5.js (never did it before) but I'd love to start! is it okay if I start working on this issue? (sorry if I mentioned you but I didn't know who to speak to)

@Bumblebee00
Copy link
Contributor

Bumblebee00 commented Jan 7, 2024

hello everyone, the modifications I did I think fixed the issue. @davepagurek I tried converting the colors to vector and using slerp, but in the end the way I did (just looping the hue value) was simpler for the following reasons:

  • slerp is spherical linear interpolation in a cartesian coordinate system, while HSL and HSB are cylindrical coordinate systems.
  • the saturation and hue are already in polar coordinate form, so we would have needed to convert them to cartesian. it's much more convenient to just modify the hue
  • the slerp function in not defined if user isn't using math module. so we would have needed to redefine it

I hope this is okay and if I did something wrong please tell me because this is my first contribution to an open source project 😄

PS The script I used in the screenshots is this:

function setup() {
  createCanvas(500, 500);
  noStroke();
  background(51);

  colorMode(HSB);
  const from = color('hsl(340, 100%, 50%)');
  const to = color('hsl(20, 100%, 50%)');

  let n = 20;
  for (let i = 0; i < n; i++) {
    const inter = lerpColor(from, to, i / (n - 1));
    fill(inter);
    rect(10 + i * 480 / n, 10, 480/n, 240);
  }

  fill(from);
  rect(10, 260, 30, 30);
  fill(to);
  rect(460, 260, 30, 30);
}

@davepagurek
Copy link
Contributor

That sounds reasonable @Bumblebee00! I was thinking of slerp just being used for the hue, but if a quick if statement + addition of 1 does the job, that sounds great to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants