Skip to content

Commit ddc668a

Browse files
feat(strands): add hue(), saturation(), brightness(), lightness() accessors
1 parent 4c63088 commit ddc668a

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

src/strands/strands_api.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,97 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
371371
return p5.strandsNode(args[0]).w;
372372
});
373373

374+
// HSB/HSL accessors — inject conversion helpers and extract channel
375+
const _hsbSnippet = `vec3 _p5_rgb2hsb(vec3 c) {
376+
vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
377+
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
378+
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
379+
float d = q.x - min(q.w, q.y);
380+
float e = 1.0e-10;
381+
return vec3(abs(q.z+(q.w-q.y)/(6.0*d+e)), d/(q.x+e), q.x);
382+
}`;
383+
384+
const _hslSnippet = `vec3 _p5_rgb2hsl(vec3 c) {
385+
float maxC = max(c.r, max(c.g, c.b));
386+
float minC = min(c.r, min(c.g, c.b));
387+
float l = (maxC + minC) / 2.0;
388+
float d = maxC - minC;
389+
float s = d < 1e-10 ? 0.0 : d/(1.0-abs(2.0*l-1.0));
390+
float h = 0.0;
391+
if (d > 1e-10) {
392+
if (maxC == c.r) h = mod((c.g-c.b)/d, 6.0)/6.0;
393+
else if (maxC == c.g) h = ((c.b-c.r)/d+2.0)/6.0;
394+
else h = ((c.r-c.g)/d+4.0)/6.0;
395+
}
396+
return vec3(h, s, l);
397+
}`;
398+
399+
function _injectSnippet(snippet) {
400+
strandsContext.vertexDeclarations.add(snippet);
401+
strandsContext.fragmentDeclarations.add(snippet);
402+
strandsContext.computeDeclarations.add(snippet);
403+
}
404+
405+
const originalHue = fn.hue;
406+
augmentFn(fn, p5, 'hue', function (...args) {
407+
if (!strandsContext.active) {
408+
return originalHue.apply(this, args);
409+
}
410+
_injectSnippet(_hslSnippet);
411+
const colorNode = p5.strandsNode(args[0]);
412+
const { id, dimension } = build.functionCallNode(
413+
strandsContext, '_p5_rgb2hsl',
414+
[fn.vec3(colorNode.x, colorNode.y, colorNode.z)],
415+
{ overloads: [{ params: [DataType.float3], returnType: DataType.float3 }] }
416+
);
417+
return createStrandsNode(id, dimension, strandsContext).x;
418+
});
419+
420+
const originalSaturation = fn.saturation;
421+
augmentFn(fn, p5, 'saturation', function (...args) {
422+
if (!strandsContext.active) {
423+
return originalSaturation.apply(this, args);
424+
}
425+
_injectSnippet(_hslSnippet);
426+
const colorNode = p5.strandsNode(args[0]);
427+
const { id, dimension } = build.functionCallNode(
428+
strandsContext, '_p5_rgb2hsl',
429+
[fn.vec3(colorNode.x, colorNode.y, colorNode.z)],
430+
{ overloads: [{ params: [DataType.float3], returnType: DataType.float3 }] }
431+
);
432+
return createStrandsNode(id, dimension, strandsContext).y;
433+
});
434+
435+
const originalBrightness = fn.brightness;
436+
augmentFn(fn, p5, 'brightness', function (...args) {
437+
if (!strandsContext.active) {
438+
return originalBrightness.apply(this, args);
439+
}
440+
_injectSnippet(_hsbSnippet);
441+
const colorNode = p5.strandsNode(args[0]);
442+
const { id, dimension } = build.functionCallNode(
443+
strandsContext, '_p5_rgb2hsb',
444+
[fn.vec3(colorNode.x, colorNode.y, colorNode.z)],
445+
{ overloads: [{ params: [DataType.float3], returnType: DataType.float3 }] }
446+
);
447+
return createStrandsNode(id, dimension, strandsContext).z;
448+
});
449+
450+
const originalLightness = fn.lightness;
451+
augmentFn(fn, p5, 'lightness', function (...args) {
452+
if (!strandsContext.active) {
453+
return originalLightness.apply(this, args);
454+
}
455+
_injectSnippet(_hslSnippet);
456+
const colorNode = p5.strandsNode(args[0]);
457+
const { id, dimension } = build.functionCallNode(
458+
strandsContext, '_p5_rgb2hsl',
459+
[fn.vec3(colorNode.x, colorNode.y, colorNode.z)],
460+
{ overloads: [{ params: [DataType.float3], returnType: DataType.float3 }] }
461+
);
462+
return createStrandsNode(id, dimension, strandsContext).z;
463+
});
464+
374465
augmentFn(fn, p5, 'getTexture', function (...rawArgs) {
375466
if (strandsContext.active) {
376467
const { id, dimension } = strandsContext.backend.createGetTextureCall(strandsContext, rawArgs);

0 commit comments

Comments
 (0)