import { mergeOver, pushUnique, λnull, Ωempty } from '../core/utilities.js';
import { makeGradient } from '../factory/gradient.js';The following functionality is shared between NoiseAsset and RdAsset objects
import { mergeOver, pushUnique, λnull, Ωempty } from '../core/utilities.js';
import { makeGradient } from '../factory/gradient.js';export default function (P = Ωempty) { let defaultAttributes = {
choke: 15,paletteStart, paletteEnd pseudo-attributes - We don’t need to use the entire palette when building a gradient; we can restrict the palette using these start and end attributes.
The cyclePalette pseudo-attribute tells the Palette object how to handle situations where the paletteStart value is greater than the paletteEnd value:
The Gradient’s delta object is not stored in the defs object; it acts in a similar way to the artefact delta object - though it is restricted to adding delta values to Number and ‘String%’ attributes.
The colors pseudo-attribute can be used to pass through an array of palette color objects to the Gradient Palette object. The data is not retained by the gradient object.
gradient.updateColor and gradient.removeColor functionsThe easing pseudo-attribute represents a transformation that will be applied to a copy of the color stops Array - this allows us to create non-linear gradients. Value is passed through to the Palette object
The precision pseudo-attribute - value is passed through to the Gradient Palette object
The colorSpace - String pseudo-attribute defines the color space to be used by the Gradient Palette’s Color object for its internal calculations - value is passed through to the Palette object
'RGB', 'HSL', 'HWB', 'XYZ', 'LAB', 'LCH', 'OKLAB', 'OKLCH' with RGB as the defaultThe returnColorAs - String pseudo-attribute defines the type of color String the Gradient Palette’s Color object will return - value is passed through to the Gradient Palette object
'RGB', 'HSL', 'HWB', 'LAB', 'LCH', 'OKLAB', 'OKLCH' with RGB as the default };
P.defs = mergeOver(P.defs, defaultAttributes);These all route get/set/setDelta attribute changes through to the Gradient object
let S = P.setters,
D = P.deltaSetters;
S.paletteStart = function (item) {
if (this.gradient) this.gradient.set({ paletteStart: item });
};
D.paletteStart = function (item) {
if (this.gradient) this.gradient.setDelta({ paletteStart: item });
};
S.paletteEnd = function (item) {
if (this.gradient) this.gradient.set({ paletteEnd: item });
};
D.paletteEnd = function (item) {
if (this.gradient) this.gradient.setDelta({ paletteEnd: item });
};
S.colors = function (item) {
if (this.gradient) this.gradient.set({ colors: item });
};
S.precision = function (item) {
if (this.gradient) this.gradient.set({ precision: item });
};
S.easing = function (item) {
if (this.gradient) this.gradient.set({ easing: item });
};
S.easingFunction = S.easing;
S.colorSpace = function (item) {
if (this.gradient) this.gradient.set({ colorSpace: item });
};
S.returnColorAs = function (item) {
if (this.gradient) this.gradient.set({ returnColorAs: item });
};
S.cyclePalette = function (item) {
if (this.gradient) this.gradient.set({ cyclePalette: item });
};
S.delta = function (items = Ωempty) {
if (this.gradient) this.gradient.set({ delta: items });
};installElement - internal function, used by the constructor
P.installElement = function (name) {
let element = document.createElement('canvas');
element.id = name;
this.element = element;
this.engine = this.element.getContext('2d');The color canvas allows us to map contour-like lines across a noise or rd asset’s output.
let color = document.createElement('canvas');
color.id = `${name}-color`;
color.width = 256;
color.height = 1;
this.colorElement = color;
this.colorEngine = this.colorElement.getContext('2d');
this.gradient = makeGradient({
name: `${name}-gradient`,
endX: '100%',
delta: {
paletteStart: 0,
paletteEnd: 0,
},
cyclePalette: false,
});
this.gradientLastUpdated = 0;
return this;
};checkSource
notifySubscribers, where dirty flags get checked and rectified P.checkSource = function (width, height) {
this.notifySubscribers();
};getData function called by Cell objects when calculating required updates to its CanvasRenderingContext2D engine, specifically for an entity’s fillStyle, strokeStyle and shadowColor attributes.
P.getData = function (entity, cell) {
this.notifySubscribers();
return this.buildStyle(cell);
};notifySubscribers - If the gradient is to be animated, then we need to update the asset at some point (generally the start) of each Display cycle by invoking this function
P.update = function () {
this.dirtyOutput = true;
};notifySubscribers, notifySubscriber - overwrites the functions defined in mixin/asset.js
P.notifySubscribers = function () {
if (this.dirtyOutput) this.cleanOutput();
this.subscribers.forEach(sub => this.notifySubscriber(sub), this);
};
P.notifySubscriber = function (sub) {
sub.sourceNaturalWidth = this.width;
sub.sourceNaturalHeight = this.height;
sub.sourceLoaded = true;
sub.source = this.element;
sub.dirtyImage = true;
sub.dirtyCopyStart = true;
sub.dirtyCopyDimensions = true;
sub.dirtyImageSubscribers = true;
};paintCanvas - internal function called by the cleanOutput function
P.paintCanvas = function () {
if (this.checkOutputValuesExist()) {
if (this.dirtyOutput) {
this.dirtyOutput = false;
const {element, engine, width, height, colorElement, colorEngine, gradient, choke, gradientLastUpdated } = this;
const palette = gradient.palette;Update the Canvas element’s dimensions - this will also clear the canvas display
element.width = width;
element.height = height;
let img = engine.getImageData(0, 0, width, height),
iData = img.data,
len = width * height,
i, v, c;
const now = Date.now();
if (gradientLastUpdated + choke < now) {
gradient.updateByDelta();
this.gradientLastUpdated = now;
}
if (palette.dirtyPalette) palette.recalculate();
let G = colorEngine.createLinearGradient(0, 0, 255, 0);
gradient.addStopsToGradient(G, gradient.paletteStart, gradient.paletteEnd, gradient.cyclePalette);
colorEngine.fillStyle = G;
colorEngine.fillRect(0, 0, 256, 1);
let gData = colorEngine.getImageData(0, 0, 256, 1).data;
for (i = 0; i < len; i++) {
v = Math.floor(this.getOutputValue(i, width) * 255) * 4;
c = i * 4;
iData[c] = gData[v];
iData[++c] = gData[++v];
iData[++c] = gData[++v];
iData[++c] = gData[++v];
}
engine.putImageData(img, 0, 0);
}
}
};Return the prototype
return P;
};