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

[@thi.ng/color] mix() updates input color #387

Closed
cdaein opened this issue Feb 28, 2023 · 3 comments
Closed

[@thi.ng/color] mix() updates input color #387

cdaein opened this issue Feb 28, 2023 · 3 comments

Comments

@cdaein
Copy link

cdaein commented Feb 28, 2023

Hi,
I am using mix() in @thi.ng/color to manually lerp colors. After using it, when I check the original input color, it has been transformed.

  import { hsv, mix } from "@thi.ng/color";

  const col1 = hsv(0, 0.0, 1);
  const col2 = hsv(0.4, 1, 1);

  const num = 4;

  console.log(col1.buf); // [0, 0, 1, 1]
  for (let i = 0; i < num; i++) {
    // use null
    const colMid = mix(null, col1, col2, i / (num - 1));
  }
  console.log(col1.buf); // [0.4, 1, 1, 1] - it should be [0, 0, 1, 1]

It works fine if I reuse the same color object:

  let colMid = hsv();
  console.log(col1.buf); // [0, 0, 1, 1]
  for (let i = 0; i < num; i++) {
    // reuse
    mix(colMid, col1, col2, i / (num - 1));
  }
  console.log(col1.buf); // [0, 0, 1, 1] correct value

I'm wondering if I am missing anything.
Thank you!

@postspectacular
Copy link
Member

postspectacular commented Mar 1, 2023

Sorry, @cdaein - this needs to be added to the docs! Since the colors are all "vector" based, the behavior of this function (and possibly others in the color package) is the same as most of the functions in the https://thi.ng/vectors package:

Each operation producing a vector result takes an output vector as first argument. If null, the vector given as 2nd argument will (usually) be used as output (i.e. for mutation).

So in your case this would mean if the 1st arg to mix() is null, then the 2nd arg (col1) will be used to store the result. The main reason for this design decision is that it allows the same set of functions to be used for in-place updates or to produce new results... The vectors pkg already has 900+ functions/operations, without that behavior it'd have to be double that! 🤯

Another important addition for these functions in the colors package (e.g. also analog(), clamp() etc.): If you want those functions to write its result into a new color object, always provide one of the same mode:

const a = hsv(0,1,1);
const b = hsv(0.5, 0.5, 0.5);

// result written to a (i.e. res === a)
const res = mix(null, a, b, 0.1);

// result written to a new hsv color
const res = mix(hsv(), a, b, 0.1);

// result written to new untyped raw color array, then wrapped as hsv
const res = hsv(mix([], a, b, 0.1));

// result written to a new color w/ same mode as `a`
// (useful if you don't know what mode/space a is in originally)
const res = mix(a.empty(), a, b, 0.1);

// similar to previous, but using ICopy interface instead
const res = mix(a.copy(), a, b, 0.1);

Btw. The last two examples are making use of the general ICopy and IEmpty interfaces, which are also implemented by many other types in various umbrella packages...

Hth! Will add docs for this ASAP - sorry for the confusion!

@cdaein
Copy link
Author

cdaein commented Mar 1, 2023

Oh, I have missed that.. Thank you very much for the clarification and the clear examples! 🙏

@postspectacular
Copy link
Member

postspectacular commented Mar 1, 2023

Not your fault to miss that little note! Just pushed a commit (cda14bf) making this behavior (hopefully) more obvious in the doc strings for all relevant functions... More pkg updates incoming (oklch support and some other improvements), new version soon!

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

No branches or pull requests

2 participants