Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions src/color/creating_reading.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export const OKLCH = 'oklch';
*/
export const RGBA = 'rgba';

// red(), green() and blue() use the display-p3 ranges for display-p3 colors and
// the RGB ranges for every other color.
const rgbMode = color => (color.mode === RGBP3 ? RGBP3 : RGB);

function creatingReading(p5, fn) {
fn.RGB = RGB;
fn.RGBP3 = RGBP3;
Expand Down Expand Up @@ -410,12 +414,16 @@ function creatingReading(p5, fn) {
return args[0]; // Do nothing if argument is already a color object.
}

// Colors reach their sketch through a holder that is cleared when the
// sketch is removed, so they don't keep a removed sketch alive.
this._colorOwner ??= { pInst: this };

const arg = Array.isArray(args[0]) ? args[0] : args;
return new Color(
arg,
this._renderer.states.colorMode,
this._renderer.states.colorMaxes[this._renderer.states.colorMode],
{ clamp: true }
{ clamp: true, owner: this._colorOwner }
);
};

Expand Down Expand Up @@ -568,8 +576,8 @@ function creatingReading(p5, fn) {
*/
fn.red = function (c) {
// p5._validateParameters('red', arguments);
// Get current red max
return this.color(c)._getRed();
const color = this.color(c);
return color._getRed(this._renderer.states.colorMaxes[rgbMode(color)][0]);
};

/**
Expand Down Expand Up @@ -721,8 +729,8 @@ function creatingReading(p5, fn) {
*/
fn.green = function (c) {
// p5._validateParameters('green', arguments);
// Get current green max
return this.color(c)._getGreen();
const color = this.color(c);
return color._getGreen(this._renderer.states.colorMaxes[rgbMode(color)][1]);
};

/**
Expand Down Expand Up @@ -874,8 +882,8 @@ function creatingReading(p5, fn) {
*/
fn.blue = function (c) {
// p5._validateParameters('blue', arguments);
// Get current blue max
return this.color(c)._getBlue();
const color = this.color(c);
return color._getBlue(this._renderer.states.colorMaxes[rgbMode(color)][2]);
};

/**
Expand Down Expand Up @@ -995,8 +1003,8 @@ function creatingReading(p5, fn) {
*/
fn.alpha = function (c) {
// p5._validateParameters('alpha', arguments);
// Get current alpha max
return this.color(c)._getAlpha();
const color = this.color(c);
return color._getAlpha(this._renderer.states.colorMaxes[color.mode][3]);
};

/**
Expand Down
112 changes: 35 additions & 77 deletions src/color/p5.Color.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,13 @@ class Color {
Color.#grayscaleMap[mode] = definition.fromGray;
}

constructor(vals, colorMode, colorMaxes, { clamp = false } = {}) {
// Holds the sketch (or graphics) that created this color so that the
// setters can read its colorMode() ranges. See the `color` addon below.
#owner;

constructor(vals, colorMode, colorMaxes, { clamp = false, owner } = {}) {
this.#owner = owner;

// This changes with the color object
this._cachedMode = colorMode || RGB;

Expand Down Expand Up @@ -332,7 +338,13 @@ class Color {
space: Color.colorMap[lerpMode]
})(amt);

return new Color(obj, mode || this.mode);
return new Color(obj, mode || this.mode, undefined, { owner: this.#owner });
}

// The colorMode() ranges of the sketch that created this color, or undefined
// if there is none or it has been removed.
_getOwnerMaxes() {
return this.#owner?.pInst?._renderer?.states?.colorMaxes;
}

/**
Expand Down Expand Up @@ -1097,82 +1109,28 @@ function color(p5, fn, lifecycles) {
p5.Color.addColorMode(OKLAB, OKLab);
p5.Color.addColorMode(OKLCH, OKLCHSpace);

lifecycles.presetup = function () {
const pInst = this;

// Decorate set methods
const setMethods = ['Red', 'Green', 'Blue', 'Alpha'];
for (let i in setMethods) {
const method = setMethods[i];
const setCopy = p5.Color.prototype['set' + method];
p5.Color.prototype['set' + method] = function (newval, max) {
max = max || pInst?._renderer?.states?.colorMaxes?.[RGB][i];
return setCopy.call(this, newval, max);
};
}

// Decorate get methods
function decorateGet(channel, modes) {
const getCopy = p5.Color.prototype['_get' + channel];
p5.Color.prototype['_get' + channel] = function (max) {
if (Object.keys(modes).includes(this.mode)) {
max =
max ||
pInst?._renderer?.states?.colorMaxes?.[this.mode][modes[this.mode]];
} else {
const defaultMode = Object.keys(modes)[0];
max =
max ||
pInst?._renderer?.states?.colorMaxes?.[defaultMode][
modes[defaultMode]
];
}

return getCopy.call(this, max);
};
// The setters take values in the range of the sketch's colorMode(), so they
// look up the ranges of the sketch that created the color. Red, green and
// blue use the RGB ranges. Alpha uses the ranges of the color's own mode, like
// alpha() does. This is done once for the shared prototype, not once per
// sketch.
const setRangeFor = channel => setter =>
function (value, max) {
const mode = channel === 3 ? this.mode : RGB;
const range = this._getOwnerMaxes()?.[mode]?.[channel];
return setter.call(this, value, max || range);
};
p5.registerDecorator('p5.Color.prototype.setRed', setRangeFor(0));
p5.registerDecorator('p5.Color.prototype.setGreen', setRangeFor(1));
p5.registerDecorator('p5.Color.prototype.setBlue', setRangeFor(2));
p5.registerDecorator('p5.Color.prototype.setAlpha', setRangeFor(3));

// Colors only reach their sketch through a holder, which is cleared here so
// that a removed sketch isn't kept in memory by its colors.
lifecycles.remove = function () {
if (this._colorOwner) {
this._colorOwner.pInst = null;
}

decorateGet('Red', {
[RGB]: 0,
[RGBP3]: 0
});
decorateGet('Green', {
[RGB]: 1,
[RGBP3]: 1
});
decorateGet('Blue', {
[RGB]: 2,
[RGBP3]: 2
});
decorateGet('Alpha', {
[RGB]: 3,
[RGBP3]: 3,
[HSB]: 3,
[HSL]: 3,
[HWB]: 3,
[LAB]: 3,
[LCH]: 3,
[OKLAB]: 3,
[OKLCH]: 3
});

decorateGet('Hue', {
[HSL]: 0,
[HSB]: 0,
[HWB]: 0,
[LCH]: 2,
[OKLCH]: 2
});
decorateGet('Saturation', {
[HSL]: 1,
[HSB]: 1
});
decorateGet('Brightness', {
[HSB]: 2
});
decorateGet('Lightness', {
[HSL]: 2
});
};
}

Expand Down
Loading