Skip to content

Instantly share code, notes, and snippets.

@0x0f0f0f
Last active May 4, 2023 19:52
Show Gist options
  • Save 0x0f0f0f/71740f86f506e9528bf1e9c946636c1f to your computer and use it in GitHub Desktop.
Save 0x0f0f0f/71740f86f506e9528bf1e9c946636c1f to your computer and use it in GitHub Desktop.
// hydra-abbreviations
// diminutives for all the functions
// by ritchse
const getHydra = function () {
const whereami = window.choo?.state?.hydra
? "editor"
: window.atom?.packages
? "atom"
: "idk";
console.log(whereami);
switch (whereami) {
case "editor":
return choo.state.hydra.hydra;
case "atom":
return global.atom.packages.loadedPackages["atom-hydra"]
.mainModule.main.hydra;
case "idk":
let _h = undefined;
_h = window._hydra?.regl ? window._hydra : _h;
_h = window.hydra?.regl ? window.hydra : _h;
_h = window.h?.regl ? window.h : _h;
_h = window.H?.regl ? window.H : _h;
_h = window.hy?.regl ? window.hy : _h;
return _h;
}
};
const loadExtensions = function () {
window._hydra = getHydra();
window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth;
{
const gS = _hydraScope.osc().constructor.prototype;
function wrapColorCombine(name, color, combine) {
gS[name] = function (arg, ...args) {
isArgument =
typeof arg == "number" ||
typeof arg == "function" ||
typeof arg == "string" ||
arg.constructor.name == "Array";
return isArgument
? this[color](arg)
: args.length
? this[combine](arg, ...args)
: this[combine](arg);
};
}
{
const noInputs = (
"abs,sign,fract," +
"sin,cos,tan,asin,acos,atan," +
"exp,log,exp2,log2,sqrt,inversesqrt"
).split(",");
noInputs.forEach((name) => {
_hydra.synth.setFunction({
name: "_" + name,
type: "color",
inputs: [],
glsl: `return ${name}(_c0);`,
});
gS[name] = gS["_" + name];
});
}
{
const singleArgument = "mod,min,max,step".split(",");
singleArgument.forEach((name) => {
_hydra.synth.setFunction({
name: "_" + name,
type: "combine",
inputs: [],
glsl: `return ${name}(_c0,_c1);`,
});
_hydra.synth.setFunction({
name: "_" + name + "_single",
type: "color",
inputs: [{ name: "_in", type: "float", default: 1 }],
glsl: `return ${name}(_c0,_in);`,
});
wrapColorCombine(name, "_" + name + "_single", "_" + name);
});
}
_hydra.synth.setFunction({
name: "_div",
type: "combine",
inputs: [],
glsl: `return _c0 / _c1;`,
});
_hydra.synth.setFunction({
name: "_div_single",
type: "color",
inputs: [{ name: "_in", type: "float", default: 1 }],
glsl: `return _c0 / _in;`,
});
wrapColorCombine("div", "_div_single", "_div");
_hydra.synth.setFunction({
name: "_add",
type: "combine",
inputs: [{ type: "float", name: "amount", default: 1 }],
glsl: `return _c0 + (_c1*amount);`,
});
_hydra.synth.setFunction({
name: "_add_single",
type: "color",
inputs: [{ name: "_in", type: "float", default: 1 }],
glsl: `return _c0 + _in;`,
});
wrapColorCombine("add", "_add_single", "_add");
_hydra.synth.setFunction({
name: "_sub",
type: "combine",
inputs: [{ type: "float", name: "amount", default: 1 }],
glsl: `return _c0 - (_c1*amount);`,
});
_hydra.synth.setFunction({
name: "_sub_single",
type: "color",
inputs: [{ name: "_in", type: "float", default: 1 }],
glsl: `return _c0 - _in;`,
});
wrapColorCombine("sub", "_sub_single", "_sub");
_hydra.synth.setFunction({
name: "_mult",
type: "combine",
inputs: [{ type: "float", name: "amount", default: 1 }],
glsl: `return _c0*(1.0-amount)+(_c0*_c1)*amount;`,
});
_hydra.synth.setFunction({
name: "_mult_single",
type: "color",
inputs: [{ name: "_in", type: "float", default: 1 }],
glsl: `return _c0 * _in;`,
});
wrapColorCombine("mult", "_mult_single", "_mult");
gS.amp = gS._mult_single;
gS.amplitude = gS.amp;
gS.offset = gS._add_single;
gS.off = gS.offset;
_hydra.synth.setFunction({
name: "bipolar",
type: "color",
inputs: [{ name: "amp", type: "float", default: 1 }],
glsl: `return ((_c0 * 2.0) - 1.0) * amp;`,
});
_hydra.synth.setFunction({
name: "unipolar",
type: "color",
inputs: [{ name: "amp", type: "float", default: 1 }],
glsl: `return ((_c0 + 1.0) * 0.5) * amp;`,
});
_hydra.synth.setFunction({
name: "range",
type: "color",
inputs: [
{ name: "_min", type: "float", default: 0 },
{ name: "_max", type: "float", default: 1 },
],
glsl: `return _c0 * (_max - _min) + _min;`,
});
_hydra.synth.setFunction({
name: "birange",
type: "color",
inputs: [
{ name: "_min", type: "float", default: 0 },
{ name: "_max", type: "float", default: 1 },
],
glsl: `return ((_c0 + 1.0) * 0.5) * (_max - _min) + _min;`,
});
_hydra.synth.setFunction({
name: "_clamp",
type: "color",
inputs: [
{ name: "_min", type: "float", default: 0 },
{ name: "_max", type: "float", default: 1 },
],
glsl: `return clamp(_c0, _min, _max);`,
});
gS.clamp = gS._clamp;
_hydra.synth.setFunction({
name: "x",
type: "src",
inputs: [
{ name: "mult", type: "float", default: 1 },
],
glsl: `return vec4(vec3(_st.x*mult),1.0);`,
});
_hydra.synth.setFunction({
name: "y",
type: "src",
inputs: [
{ name: "mult", type: "float", default: 1 },
],
glsl: `return vec4(vec3(_st.y*mult),1.0);`,
});
_hydra.synth.setFunction({
name: "length",
type: "src",
inputs: [
{ name: "mult", type: "float", default: 1 },
],
glsl: `return vec4(vec3(length(_st*mult)),1.0);`,
});
_hydra.synth.setFunction({
name: "distance",
type: "src",
inputs: [
{ name: "px", type: "float", default: 0 },
{ name: "py", type: "float", default: 0 },
],
glsl: `return vec4(vec3(length(_st,vec2(px,py))),1.0);`,
});
_hydra.synth.setFunction({
name: "xCenter",
type: "src",
inputs: [
{ name: "mult", type: "float", default: 1 },
],
glsl: `return vec4(vec3((0.5-_st.x)*mult),1.0);`,
});
_hydra.synth.setFunction({
name: "yCenter",
type: "src",
inputs: [
{ name: "mult", type: "float", default: 1 },
],
glsl: `return vec4(vec3((0.5-_st.y)*mult),1.0);`,
});
_hydra.synth.setFunction({
name: "lengthCenter",
type: "src",
inputs: [
{ name: "mult", type: "float", default: 1 },
],
glsl: `return vec4(vec3(length((vec2(0.5)-_st)*mult)),1.0);`,
});
_hydra.synth.setFunction({
name: "distanceCenter",
type: "src",
inputs: [
{ name: "px", type: "float", default: 0 },
{ name: "py", type: "float", default: 0 },
],
glsl: `return vec4(vec3(length(vec2(0.5)-_st,vec2(0.5)-vec2(px,py))),1.0);`,
});
}
window.hydraArrays = {};
hydraArrays.newOperator = function (self, f) {
return function (arr) {
for (let i = 0; i < self.length; i++) {
if (Array.isArray(arr)) {
self[i] = arr[i] ? f(self[i], arr[i]) : self[i];
} else {
self[i] = f(self[i], arr);
}
}
return self;
};
};
hydraArrays.newWrapOperator = function (self, f) {
return function (arr) {
for (let i = 0; i < self.length; i++) {
if (Array.isArray(arr)) {
self[i] = f(self[i], arr[i % arr.length]);
} else {
self[i] = f(self[i], arr);
}
}
return self;
};
};
// operators
{
const operators = {
add: (x, y) => x + y,
sub: (x, y) => x - y,
div: (x, y) => x / y,
mult: (x, y) => x * y,
mod: (x, y) => x % y,
};
Object.entries(operators).forEach(function ([op, f]) {
Array.prototype[op] = function (arr) {
return hydraArrays.newOperator(this, f)(arr);
};
Array.prototype[op + "Wrap"] = function (arr) {
return hydraArrays.newWrapOperator(this, f)(arr);
};
});
}
// methods
Array.prototype.shuffle = function () {
return this.sort(() => Math.random() - 0.5);
};
Array.prototype.zfill = function (l, z = 0) {
const _l = this.length;
for (let i = 0; i < l - _l; i++) this.push(z);
return this;
};
Array.prototype.rotate = function (n) {
const len = this.length;
this.push(...this.splice(0, ((-n % len) + len) % len));
return this;
};
Array.prototype.rot = Array.prototype.rotate;
// generators
Array.random = function (l = 10, min = 0, max = 1) {
return Array.from(
{ length: l },
() => Math.random() * (max - min) + min
);
};
Array.range = function (start, end, step = 1) {
if (step === 0) return [];
const length = Math.ceil((end - start) / step);
return Array.from({ length }, (_, i) => start + i * step).filter(
(n) => n <= end
);
};
Array.run = function (end = 10, step = 1) {
return Array.range(0, end, step);
};
{
const blendingFuncs = {
add2: "min(base+blend,1.0)",
average: "(base+blend)/2.0",
colorBurn: "(blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0)",
colorDodge: "(blend==1.0)?blend:min(base/(1.0-blend),1.0)",
darken: "min(blend,base)",
difference: "abs(base-blend)",
divide: "min(base/(blend+0.00000001),1.0)",
exclusion: "base+blend-2.0*base*blend",
glow: "(base==1.0)?base:min(blend*blend/(1.0-base),1.0)",
hardLight:
"blend<0.5?(2.0*blend*base):(1.0-2.0*(1.0-blend)*(1.0-base))",
hardMix:
"(((blend<0.5)?((blend==0.0)?(blend):max((1.0-((1.0-base)/(2.0*blend))),0.0)):(((2.0*(blend-0.5))==1.0)?(2.0*(blend-0.5)):min(base/(1.0-(2.0*(blend-0.5))),1.0)))<0.5)?0.0:1.0",
lighten: "max(blend,base)",
linearBurn: "max(base+blend-1.0,0.0)",
linearDodge: "min(base+blend,1.0)",
linearLight:
"blend<0.5?(max(base+(2.0*blend)-1.0,0.0)):min(base+(2.0*(blend-0.5)),1.0)",
multiply: "base*blend",
negation: "1.0-abs(1.0-base-blend)",
overlay: "base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend))",
phoenix: "min(base,blend)-max(base,blend)+vec3(1.0)",
pinLight: "(blend<0.5)?min(base,2.0*blend):max(base,2.0*(blend-0.5))",
reflect: "(blend==1.0)?blend:min(base*base/(1.0-blend),1.0)",
screen: "1.0-((1.0-base)*(1.0-blend))",
softLight:
"(blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend))",
subtract: "max(base+blend-1.0,0.0)",
vividLight:
"(blend<0.5)?((blend==0.0)?(blend):max((1.0-((1.0-base)/(2.0*blend))),0.0)):(((2.0*(blend-0.5))==1.0)?(2.0*(blend-0.5)):min(base/(1.0-(2.0*(blend-0.5))),1.0))",
};
Object.entries(blendingFuncs).forEach(([mode, def]) => {
let glsl =
"vec3 rgb;\n\n" +
//"_c0 = clamp(_c0, 0.0, 1.0);" +
//"_c1 = clamp(_c1, 0.0, 1.0);" +
["r", "g", "b"].reduce((acc, elem) => {
return (
acc +
(`rgb.${elem} = ` +
def
.replaceAll("base", "_c0." + elem)
.replaceAll("blend", "_c1." + elem) +
";\n\n")
);
}, "") +
"_c1.a *= amount;" +
"vec4 blended = vec4(mix(_c0.rgb, rgb, _c1.a), 1.0);" +
"vec4 over = _c1+(_c0*(1.0-_c1.a));" +
"return mix(blended, over, 1.0-_c0.a);";
_hydra.synth.setFunction({
name: mode,
type: "combine",
inputs: [{ name: "amount", type: "float", default: 1 }],
glsl,
});
});
}
_hydra.synth.setFunction({
name: "layer",
type: "combine",
inputs: [
{
type: "float",
name: "amount",
default: 1,
},
],
glsl: `
_c0.a = clamp(_c0.a, 0.0, 1.0);
_c1.a = clamp(_c1.a, 0.0, 1.0);
return _c1+(_c0*(1.0-_c1.a));
`,
});
_hydra.synth.setFunction({
name: "mask",
type: "combine",
inputs: [],
glsl: `
float a = _luminance(_c1.rgb);
return vec4(_c0.rgb*a, _c0.a*a);
`,
});
_hydra.synth.setFunction({
name: "luma",
type: "color",
inputs: [
{
type: "float",
name: "threshold",
default: 0.5,
},
{
type: "float",
name: "tolerance",
default: 0.1,
},
],
glsl: `
float a = smoothstep(threshold-(tolerance+0.0000001), threshold+(tolerance+0.0000001), _luminance(_c0.rgb*_c0.a));
return vec4(_c0.rgb*a, a);
`,
});
_hydra.synth.setFunction({
name: "clamp",
type: "color",
inputs: [],
glsl: `
return clamp(_c0,0.0,1.0);
`,
});
_hydra.synth.setFunction({
name: "premultiply",
type: "color",
inputs: [],
glsl: `
_c0 = clamp(_c0,0.0,1.0);
return vec4(_c0.rgb*_c0.a,_c0.a);
`,
});
{
const gS = _hydraScope.osc().constructor.prototype;
gS.pm = gS.premultiply;
gS.negate = gS.negation;
}
_hydraScope.canvas = window._hydra.canvas;
_hydraScope.canvas.setLinear = function () {
this.style.imageRendering = "auto";
};
_hydraScope.canvas.setNearest = function () {
this.style.imageRendering = "pixelated";
};
_hydraScope.canvas.setFullscreen = function (full = true) {
const set = full ? "100%" : "";
this.style.width = set;
this.style.height = set;
};
_hydraScope.canvas.setAlign = function (align = "right") {
this.parentElement.style["text-align"] = align;
};
_hydraScope.canvas.setRelativeSize = function (ratio) {
this.style.width = "" + width * ratio + "px";
this.style.height = "" + height * ratio + "px";
};
window.gS = _hydraScope.osc().constructor.prototype;
{
const hcs = {};
hcs.colorspaces = [
{
name: "rgb",
elems: ["r", "g", "b", "a"],
to: "r = _r; g = _g; b = _b; a = _a;",
from: "_r = r; _g = g; _b = b; _a = a;",
},
{
name: "cmyk",
elems: ["c", "m", "y", "k"],
to: `
k = 1.0-max(_r,max(_g,_b));
c = (1.0-_r-k) / (1.0-k);
m = (1.0-_g-k) / (1.0-k);
y = (1.0-_b-k) / (1.0-k);
`,
from: `
_r = (1.0-c)*(1.0-k);
_g = (1.0-m)*(1.0-k);
_b = (1.0-y)*(1.0-k);
`,
},
{
name: "hsv",
elems: ["h", "s", "v"],
to: `
vec3 _hsv = _rgbToHsv(vec3(_r,_g,_b));
h = _hsv.x; s = _hsv.y; v = _hsv.z;
`,
from: `
vec3 _rgb = _hsvToRgb(vec3(h,s,v));
_r = _rgb.r; _g = _rgb.g; _b = _rgb.b;
`,
},
{
name: "hsl",
elems: ["h", "s", "l"],
to: `
vec3 _hsv = _rgbToHsv(vec3(_r,_g,_b));
h = _hsv.x;
l = _hsv.z*(1.0-(_hsv.y*0.5));
s = (_hsv.z-l)/(min(l,1.0-l));
s *= step(-l,-0.000001)*step(l,0.999999);
`,
from: `
_hsv.x = h;
_hsv.z = l + (s*min(l,1.0-l));
_hsv.y = 2.0*(1.0-(l/_hsv.z))*step(-_hsv.z,-0.000001);
vec3 _rgb = _hsvToRgb(_hsv);
_r = _rgb.r; _g = _rgb.g; _b = _rgb.b;
`,
},
{
name: "yuv",
elems: ["y", "u", "v"],
to: `
mat3 rgb2yuv = mat3(0.2126, 0.7152, 0.0722, -0.09991, -0.33609, 0.43600, 0.615, -0.5586, -0.05639);
vec3 _yuv = vec3(_r,_g,_b) * rgb2yuv;
y = _yuv.x; u = _yuv.y; v = _yuv.z;
`,
from: `
mat3 yuv2rgb = mat3(1.0, 0.0, 1.28033, 1.0, -0.21482, -0.38059, 1.0, 2.12798, 0.0);
vec3 _rgb = vec3(y,u,v) * yuv2rgb;
_r = _rgb.r; _g = _rgb.g; _b = _rgb.b;
`,
},
{
name: "yiq",
elems: ["y", "i", "q"],
to: `
mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.5959, -0.2746, -0.3213, 0.2115, -0.5227, 0.3112);
vec3 _yiq = vec3(_r,_g,_b) * rgb2yiq;
y = _yiq.x; i = _yiq.y; q = _yiq.z;
`,
from: `
mat3 yiq2rgb = mat3(1.0, 0.956, 0.619, 1.0, -0.272, -0.647, 1.0, -1.106, 1.703);
vec3 _rgb = vec3(y,i,q) * yiq2rgb;
_r = _rgb.r; _g = _rgb.g; _b = _rgb.b;
`,
},
];
// utils
hcs.generateInputAssignment = function (elems, format) {
return format
? elems
.map((el) => {
let assign = format;
if (
"yuvalpha,yiqalpha".includes(elems.join("")) &&
"uviq".includes(el) &&
assign.includes("1.0 - #el")
) {
assign = assign.replaceAll("1.0 - #el", "0.0 - #el");
} // TODO: find a better solution for this patch on inversion
return assign
.replaceAll("#el", el)
.replaceAll("#in", "in_" + el);
})
.join("")
: "";
};
hcs.generateDirectAssignment = function (elems, tofrom) {
const rgba = ["_r", "_g", "_b", "_a"];
return tofrom == "to"
? elems.map((el, i) => rgba[i] + " = " + el + ";").join("")
: elems.map((el, i) => el + " = " + rgba[i] + ";").join("");
};
hcs.generateDeclarations = function (elems, type = "float") {
return elems.map((el) => type + " " + el + ";\n").join("");
};
// functions that use all elems
hcs.generateFunction = function ({
colorspace,
sufix,
type,
assignmentFormat,
inputDefault,
alphaDefault = 1,
tofrom,
}) {
const name = colorspace.name + (sufix ? "_" + sufix : "");
const hasColorInput = ["color", "combine"].includes(type);
const isRgb = "rgba".includes(colorspace.name);
const elems =
isRgb || tofrom
? colorspace.elems
: colorspace.elems.concat("alpha");
const inputs = assignmentFormat
? elems.map((el) => ({
type: "float",
name: "in_" + el,
default: inputDefault,
}))
: [];
inputs.length ? (inputs.at(-1).default = alphaDefault) : null;
const rgbaDeclarations = hcs.generateDeclarations([
"_r",
"_g",
"_b",
"_a",
]);
const rgbaAssignments = hasColorInput
? "_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;"
: "";
const elemDeclarations =
hcs.generateDeclarations(elems) +
(isRgb || tofrom ? "" : "alpha = _a;");
const to = hasColorInput || tofrom == "to" ? colorspace.to : "";
const elemAssignments = assignmentFormat
? hcs.generateInputAssignment(elems, assignmentFormat)
: hcs.generateDirectAssignment(elems, tofrom);
const from = !tofrom || tofrom == "from" ? colorspace.from : "";
const returner =
(isRgb || tofrom ? "" : "_a = alpha;") +
"return vec4(_r,_g,_b,_a);";
const glsl =
rgbaDeclarations +
rgbaAssignments +
elemDeclarations +
to +
elemAssignments +
from +
returner;
return { name: name, type: type, inputs: inputs, glsl: glsl };
};
hcs.generateColorFunction = (cs) =>
hcs.generateFunction({
colorspace: cs,
sufix: "color",
type: "color",
assignmentFormat: "#el *= #in;",
inputDefault: 1,
alphaDefault: 1,
});
hcs.generateOffsetFunction = (cs) =>
hcs.generateFunction({
colorspace: cs,
sufix: "offset",
type: "color",
assignmentFormat: "#el += #in;",
inputDefault: 0,
alphaDefault: 0,
});
hcs.generateSolidFunction = (cs) =>
hcs.generateFunction({
colorspace: cs,
sufix: "solid",
type: "src",
assignmentFormat: "#el = #in;",
inputDefault: 0,
alphaDefault: 1,
});
hcs.generateToFunction = (cs) =>
hcs.generateFunction({
colorspace: cs,
sufix: "to",
type: "color",
assignmentFormat: undefined,
inputDefault: 0,
alphaDefault: 1,
tofrom: "to",
});
hcs.generateFromFunction = (cs) =>
hcs.generateFunction({
colorspace: cs,
sufix: "from",
type: "color",
assignmentFormat: undefined,
inputDefault: 0,
alphaDefault: 1,
tofrom: "from",
});
hcs.generateInvertFunction = (cs) =>
hcs.generateFunction({
colorspace: cs,
sufix: "invert",
type: "color",
assignmentFormat: "#el = mix(#el, 1.0 - #el, #in);",
inputDefault: 1,
alphaDefault: 0,
});
// elem functions
hcs.generateElementFunction = function (colorspace, elem) {
const name = colorspace.name + "_" + elem;
const type = "color";
const rgbaDeclarations = hcs.generateDeclarations([
"_r",
"_g",
"_b",
"_a",
]);
const rgbaAssignments =
"_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;";
const elemDeclarations = hcs.generateDeclarations(colorspace.elems);
const to = colorspace.to;
const returner = "return vec4(vec3(" + elem + "),1.0);";
const glsl =
rgbaDeclarations +
rgbaAssignments +
elemDeclarations +
to +
returner;
return { name: name, type: type, inputs: [], glsl: glsl };
};
hcs.generateElementFunctions = (cs) =>
cs.elems.map((elem) => hcs.generateElementFunction(cs, elem));
hcs.generateSetElementFunction = function ({
colorspace,
elem,
sufix,
assignmentFormat = "#el = #in;",
inputDefault = 1,
}) {
const name = colorspace.name + "_" + elem + "_" + sufix;
const type = "color";
const inputs = [
{ type: "float", name: "in_" + elem, default: inputDefault },
];
const rgbaDeclarations = hcs.generateDeclarations([
"_r",
"_g",
"_b",
"_a",
]);
const rgbaAssignments =
"_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;";
const elemDeclarations = hcs.generateDeclarations(colorspace.elems);
const to = colorspace.to;
const elemAssignment = hcs.generateInputAssignment(
[elem],
assignmentFormat
);
const from = colorspace.from;
const returner = "return vec4(_r,_g,_b,_a);";
const glsl =
rgbaDeclarations +
rgbaAssignments +
elemDeclarations +
to +
elemAssignment +
from +
returner;
return { name: name, type: type, inputs: inputs, glsl: glsl };
};
hcs.generateSetElementFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateSetElementFunction({
colorspace: cs,
elem,
sufix: "set",
assignmentFormat: "#el = #in;",
})
);
hcs.generateOffsetElementFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateSetElementFunction({
colorspace: cs,
elem,
sufix: "offset",
assignmentFormat: "#el += #in;",
inputDefault: 1,
})
);
hcs.generateMultElementFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateSetElementFunction({
colorspace: cs,
elem,
sufix: "mult",
assignmentFormat: "#el *= #in;",
inputDefault: 1,
})
);
hcs.generateInvertElementFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateSetElementFunction({
colorspace: cs,
elem,
sufix: "invert",
assignmentFormat: "#el = mix(#el, 1.0 - #el, #in);",
inputDefault: 1,
})
);
hcs.generateCombineElementFunction = function ({
colorspace,
elem,
sufix,
assignmentFormat = "#el = _c1.r;",
inputDefault = 1,
}) {
const name = colorspace.name + "_" + elem + "_" + sufix;
const type = "combine";
const inputs = [{ type: "float", name: "_amt", default: inputDefault }];
const rgbaDeclarations = hcs.generateDeclarations([
"_r",
"_g",
"_b",
"_a",
]);
const rgbaAssignments =
"_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;";
const elemDeclarations = hcs.generateDeclarations(colorspace.elems);
const to = colorspace.to;
const elemAssignment = hcs.generateInputAssignment(
[elem],
assignmentFormat
);
const from = colorspace.from;
const returner = "return vec4(_r,_g,_b,_a);";
const glsl =
rgbaDeclarations +
rgbaAssignments +
elemDeclarations +
to +
elemAssignment +
from +
returner;
return { name: name, type: type, inputs: inputs, glsl: glsl };
};
hcs.generateSetElementFromFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateCombineElementFunction({
colorspace: cs,
elem,
sufix: "from",
assignmentFormat: "#el = mix(#el,_c1.r,_amt);",
inputDefault: 1,
})
);
hcs.generateOffsetElementFromFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateCombineElementFunction({
colorspace: cs,
elem,
sufix: "offset_from",
assignmentFormat: "#el += _c1.r*_amt;",
inputDefault: 1,
})
);
hcs.generateMultElementFromFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateCombineElementFunction({
colorspace: cs,
elem,
sufix: "mult_from",
assignmentFormat: "#el *= _c1.r*_amt;",
inputDefault: 1,
})
);
// keying
hcs.generateKeyingElementFunction = function ({ colorspace, elem }) {
const name = colorspace.name + "_" + elem + "_" + "key";
const type = "color";
const inputs = [
{ type: "float", name: "_th0", default: 0.5 },
{ type: "float", name: "_t0", default: 0.05 },
{ type: "float", name: "_th1", default: 0 },
{ type: "float", name: "_t1", default: 0 },
];
const isRgb = "rgba".includes(colorspace.name);
const rgbaDeclarations = hcs.generateDeclarations([
"_r",
"_g",
"_b",
"_a",
]);
const rgbaAssignments =
"_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;";
const elemDeclarations = hcs.generateDeclarations(colorspace.elems);
const to = colorspace.to;
const keying = (
"float _key = smoothstep(_th0-(_t0+0.0000001), _th0+(_t0+0.0000001), #elem);" +
"_th1 = 1.0 - _th1 + 0.0000001; _key *= smoothstep(_th1-(-_t1-0.0000001), _th1+(-_t1-0.0000001), #elem);" +
(isRgb ? "a" : "_a") +
" *= _key;"
).replaceAll("#elem", elem);
const from = colorspace.from;
const returner = "return vec4(_r,_g,_b,_a);";
const glsl =
rgbaDeclarations +
rgbaAssignments +
elemDeclarations +
to +
keying +
from +
returner;
return { name: name, type: type, inputs: inputs, glsl: glsl };
};
hcs.generateKeyingElementFunctions = (cs) =>
cs.elems.map((elem) =>
hcs.generateKeyingElementFunction({
colorspace: cs,
elem,
})
);
// updaters
hcs.updateFunctions = function () {
[]
.concat(
hcs.colorspaces.map(hcs.generateColorFunction),
hcs.colorspaces.map(hcs.generateOffsetFunction),
hcs.colorspaces.map(hcs.generateSolidFunction),
hcs.colorspaces.map(hcs.generateToFunction),
hcs.colorspaces.map(hcs.generateFromFunction),
hcs.colorspaces.map(hcs.generateInvertFunction),
hcs.colorspaces.map(hcs.generateElementFunctions),
hcs.colorspaces.map(hcs.generateSetElementFunctions),
hcs.colorspaces.map(hcs.generateOffsetElementFunctions),
hcs.colorspaces.map(hcs.generateMultElementFunctions),
hcs.colorspaces.map(hcs.generateInvertElementFunctions),
hcs.colorspaces.map(hcs.generateSetElementFromFunctions),
hcs.colorspaces.map(hcs.generateOffsetElementFromFunctions),
hcs.colorspaces.map(hcs.generateMultElementFromFunctions),
hcs.colorspaces.map(hcs.generateKeyingElementFunctions)
)
.flat(99)
.filter((x) => x)
.forEach((x) => _hydra.synth.setFunction(x));
};
hcs.cloneGlslSource = function (_tex) {
const tex = Object.assign({}, _tex);
Object.setPrototypeOf(tex, gS);
tex.transforms = Array.from(_tex.transforms);
return tex;
};
hcs.updateWithFunctions = function () {
hcs.colorspaces.forEach((cs) => {
cs.elems.forEach((el) => {
gS[cs.name + "_" + el + "_" + "with"] = function (f) {
const tex = hcs.cloneGlslSource(this);
return this[cs.name + "_" + el + "_" + "from"](
f(tex)[cs.name + "_" + el]()
);
};
});
});
};
hcs.update = function () {
hcs.updateFunctions();
hcs.updateWithFunctions();
hcs.colorspaces.forEach((cs) => {
let getterDefinition =
"Object.defineProperty(gS, '#cs', { configurable: true, get: function() {" +
"const func = this.#cs_color.bind(this);";
getterDefinition +=
"const props = {" +
"color: this.#cs_color.bind(this)," +
"offset: this.#cs_offset.bind(this)," +
"to: this.#cs_to.bind(this)," +
"from: this.#cs_from.bind(this)," +
"invert: this.#cs_invert.bind(this),";
cs.elems.forEach((elem) => {
getterDefinition +=
"#elem: this.#cs_#elem.bind(this)," +
"#elemSet: this.#cs_#elem_set.bind(this)," +
"#elemOffset: this.#cs_#elem_offset.bind(this)," +
"#elemMult: this.#cs_#elem_mult.bind(this)," +
"#elemInvert: this.#cs_#elem_invert.bind(this)," +
"#elemFrom: this.#cs_#elem_from.bind(this)," +
"#elemOffsetFrom: this.#cs_#elem_offset_from.bind(this)," +
"#elemMultFrom: this.#cs_#elem_mult_from.bind(this)," +
"#elemKey: this.#cs_#elem_key.bind(this)," +
"#elemWith: this.#cs_#elem_with.bind(this),";
getterDefinition = getterDefinition.replaceAll("#elem", elem);
});
getterDefinition += "};";
getterDefinition +=
"Object.assign(func,props);" + "return func; }, });";
getterDefinition += "_hydraScope.#cs = _hydraScope.#cs_solid;";
getterDefinition = getterDefinition.replaceAll("#cs", cs.name);
window.eval(getterDefinition);
});
};
hcs.update();
window.hydraColorspaces = {};
hydraColorspaces.colorspaces = hcs.colorspaces;
hydraColorspaces.update = hcs.update.bind(hcs);
}
{
async function main() {
await _hydraScope.loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"
);
await _hydraScope.loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/glsl.min.js"
);
await _hydraScope.loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify.js"
);
hljs.configure({ ignoreUnescapedHTML: true });
const brPlugin = {
"before:highlightBlock": ({ block }) => {
block.innerHTML = block.innerHTML
.replace(/\n/g, "")
.replace(/<br[ /]*>/g, "\n");
},
"after:highlightBlock": ({ result }) => {
result.value = result.value.replace(/\n/g, "<br>");
},
};
hljs.addPlugin(brPlugin);
const link = document.createElement("link");
link.href =
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/base16/tomorrow-night.min.css";
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);
{
function cleanCode(code) {
return js_beautify(code).replace(/\n{2,}/gm, "\n\n") + "\n\n";
}
function logHighlightedCode(glslSource, output) {
let passes = glslSource.glsl();
let code = passes[0].frag;
document.getElementById("hydra-debug")?.remove();
const pre = document.createElement("pre");
pre.className = "hljs";
pre.style.position = "sticky";
pre.style.height = "96%";
pre.style.padding = "4%";
pre.style.overflow = "scroll";
const codeElement = document.createElement("code");
codeElement.className = "language-glsl";
pre.appendChild(codeElement);
codeElement.innerText = cleanCode(code)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
hljs.highlightElement(pre, {
language: "glsl",
ignoreIllegals: true,
});
const wrapper = document.createElement("div");
wrapper.id = "hydra-debug";
wrapper.style.zIndex = 999;
wrapper.style.overflow = "hidden";
wrapper.style.position = "fixed";
wrapper.style.width = "40%";
wrapper.style.height = "90%";
wrapper.style.left = "58%";
wrapper.style.top = "5%";
wrapper.style.fontSize = "14px";
const close = document.createElement("button");
close.innerText = "x";
close.style.position = "absolute";
close.style.right = "0px";
close.style.top = "2%";
close.style.fontSize = "20px";
close.style.backgroundColor = "white";
close.style.color = "black";
close.style.border = "none";
close.onclick = () => {
document.getElementById("hydra-debug")?.remove();
};
wrapper.appendChild(pre);
wrapper.appendChild(close);
if (output) {
pre.contentEditable = "true";
const run = document.createElement("button");
run.innerText = ">";
run.style.position = "absolute";
run.style.right = "30px";
run.style.top = "2%";
run.style.fontSize = "20px";
run.style.backgroundColor = "white";
run.style.color = "black";
run.style.border = "none";
run.onclick = () => {
codeElement.innerText = cleanCode(pre.innerText)
pre.innerHTML = "";
pre.appendChild(codeElement);
hljs.highlightElement(pre, {
language: "glsl",
ignoreIllegals: true,
});
passes[0].frag = pre.innerText
.replace("&amp;", "&")
.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&quot;", '"')
.replace("&#039;", "'");
output.render(passes);
};
wrapper.appendChild(run);
}
document.body.append(wrapper);
}
const gS = _hydraScope.osc().constructor.prototype;
gS.debug = function (output) {
logHighlightedCode(this, output);
return output ? this.out(output) : this;
};
}
}
main();
}
[
{
name: "mirrorX",
type: "coord",
inputs: [
{
type: "float",
name: "pos",
default: 0,
},
{
type: "float",
name: "coverage",
default: 1,
},
],
glsl: `_st.x = (0.0-abs(fract(_st.x/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`,
},
{
name: "mirrorY",
type: "coord",
inputs: [
{
type: "float",
name: "pos",
default: 0,
},
{
type: "float",
name: "coverage",
default: 1,
},
],
glsl: `_st.y = (0.0-abs(fract(_st.y/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`,
},
{
name: "mirrorX2",
type: "coord",
inputs: [
{
type: "float",
name: "pos",
default: 0,
},
{
type: "float",
name: "coverage",
default: 1,
},
],
glsl: `_st.x = (abs(fract(_st.x/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`,
},
{
name: "mirrorY2",
type: "coord",
inputs: [
{
type: "float",
name: "pos",
default: 0,
},
{
type: "float",
name: "coverage",
default: 1,
},
],
glsl: `_st.y = (0.0-abs(fract(_st.y/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`,
},
{
name: "mirrorWrap",
type: "coord",
inputs: [],
glsl: `return -abs(fract(_st/2.0)*2.0-1.0)+1.0;`,
},
{
name: "inversion",
type: "coord",
inputs: [],
glsl: `_st /= dot(_st,_st); return _st;`,
},
].forEach((x) => _hydra.synth.setFunction(x));
// https://stackoverflow.com/questions/48234696/how-to-put-a-gif-with-canvas
window.GIF = function () {
// **NOT** for commercial use.
var timerID; // timer handle for set time out usage
var st; // holds the stream object when loading.
var interlaceOffsets = [0, 4, 2, 1]; // used in de-interlacing.
var interlaceSteps = [8, 8, 4, 2];
var interlacedBufSize; // this holds a buffer to de interlace. Created on the first frame and when size changed
var deinterlaceBuf;
var pixelBufSize; // this holds a buffer for pixels. Created on the first frame and when size changed
var pixelBuf;
const GIF_FILE = {
// gif file data headers
GCExt: 0xf9,
COMMENT: 0xfe,
APPExt: 0xff,
UNKNOWN: 0x01, // not sure what this is but need to skip it in parser
IMAGE: 0x2c,
EOF: 59, // This is entered as decimal
EXT: 0x21,
};
// simple buffered stream used to read from the file
var Stream = function (data) {
this.data = new Uint8ClampedArray(data);
this.pos = 0;
var len = this.data.length;
this.getString = function (count) {
// returns a string from current pos of len count
var s = "";
while (count--) {
s += String.fromCharCode(this.data[this.pos++]);
}
return s;
};
this.readSubBlocks = function () {
// reads a set of blocks as a string
var size,
count,
data = "";
do {
count = size = this.data[this.pos++];
while (count--) {
data += String.fromCharCode(this.data[this.pos++]);
}
} while (size !== 0 && this.pos < len);
return data;
};
this.readSubBlocksB = function () {
// reads a set of blocks as binary
var size,
count,
data = [];
do {
count = size = this.data[this.pos++];
while (count--) {
data.push(this.data[this.pos++]);
}
} while (size !== 0 && this.pos < len);
return data;
};
};
// LZW decoder uncompressed each frames pixels
// this needs to be optimised.
// minSize is the min dictionary as powers of two
// size and data is the compressed pixels
function lzwDecode(minSize, data) {
var i, pixelPos, pos, clear, eod, size, done, dic, code, last, d, len;
pos = pixelPos = 0;
dic = [];
clear = 1 << minSize;
eod = clear + 1;
size = minSize + 1;
done = false;
while (!done) {
// JavaScript optimisers like a clear exit though I never use 'done' apart from fooling the optimiser
last = code;
code = 0;
for (i = 0; i < size; i++) {
if (data[pos >> 3] & (1 << (pos & 7))) {
code |= 1 << i;
}
pos++;
}
if (code === clear) {
// clear and reset the dictionary
dic = [];
size = minSize + 1;
for (i = 0; i < clear; i++) {
dic[i] = [i];
}
dic[clear] = [];
dic[eod] = null;
} else {
if (code === eod) {
done = true;
return;
}
if (code >= dic.length) {
dic.push(dic[last].concat(dic[last][0]));
} else if (last !== clear) {
dic.push(dic[last].concat(dic[code][0]));
}
d = dic[code];
len = d.length;
for (i = 0; i < len; i++) {
pixelBuf[pixelPos++] = d[i];
}
if (dic.length === 1 << size && size < 12) {
size++;
}
}
}
}
function parseColourTable(count) {
// get a colour table of length count Each entry is 3 bytes, for RGB.
var colours = [];
for (var i = 0; i < count; i++) {
colours.push([
st.data[st.pos++],
st.data[st.pos++],
st.data[st.pos++],
]);
}
return colours;
}
function parse() {
// read the header. This is the starting point of the decode and async calls parseBlock
var bitField;
st.pos += 6;
gif.width = st.data[st.pos++] + (st.data[st.pos++] << 8);
gif.height = st.data[st.pos++] + (st.data[st.pos++] << 8);
bitField = st.data[st.pos++];
gif.colorRes = (bitField & 0b1110000) >> 4;
gif.globalColourCount = 1 << ((bitField & 0b111) + 1);
gif.bgColourIndex = st.data[st.pos++];
st.pos++; // ignoring pixel aspect ratio. if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
if (bitField & 0b10000000) {
gif.globalColourTable = parseColourTable(gif.globalColourCount);
} // global colour flag
setTimeout(parseBlock, 0);
}
function parseAppExt() {
// get application specific data. Netscape added iterations and terminator. Ignoring that
st.pos += 1;
if ("NETSCAPE" === st.getString(8)) {
st.pos += 8;
} // ignoring this data. iterations (word) and terminator (byte)
else {
st.pos += 3; // 3 bytes of string usually "2.0" when identifier is NETSCAPE
st.readSubBlocks(); // unknown app extension
}
}
function parseGCExt() {
// get GC data
var bitField;
st.pos++;
bitField = st.data[st.pos++];
gif.disposalMethod = (bitField & 0b11100) >> 2;
gif.transparencyGiven = bitField & 0b1 ? true : false; // ignoring bit two that is marked as userInput???
gif.delayTime = st.data[st.pos++] + (st.data[st.pos++] << 8);
gif.transparencyIndex = st.data[st.pos++];
st.pos++;
}
function parseImg() {
// decodes image data to create the indexed pixel image
var deinterlace, frame, bitField;
deinterlace = function (width) {
// de interlace pixel data if needed
var lines, fromLine, pass, toline;
lines = pixelBufSize / width;
fromLine = 0;
if (interlacedBufSize !== pixelBufSize) {
// create the buffer if size changed or undefined.
deinterlaceBuf = new Uint8Array(pixelBufSize);
interlacedBufSize = pixelBufSize;
}
for (pass = 0; pass < 4; pass++) {
for (
toLine = interlaceOffsets[pass];
toLine < lines;
toLine += interlaceSteps[pass]
) {
deinterlaceBuf.set(
pixelBuf.subarray(fromLine, fromLine + width),
toLine * width
);
fromLine += width;
}
}
};
frame = {};
gif.frames.push(frame);
frame.disposalMethod = gif.disposalMethod;
frame.time = gif.length;
frame.delay = gif.delayTime * 10;
gif.length += frame.delay;
if (gif.transparencyGiven) {
frame.transparencyIndex = gif.transparencyIndex;
} else {
frame.transparencyIndex = undefined;
}
frame.leftPos = st.data[st.pos++] + (st.data[st.pos++] << 8);
frame.topPos = st.data[st.pos++] + (st.data[st.pos++] << 8);
frame.width = st.data[st.pos++] + (st.data[st.pos++] << 8);
frame.height = st.data[st.pos++] + (st.data[st.pos++] << 8);
bitField = st.data[st.pos++];
frame.localColourTableFlag = bitField & 0b10000000 ? true : false;
if (frame.localColourTableFlag) {
frame.localColourTable = parseColourTable(
1 << ((bitField & 0b111) + 1)
);
}
if (pixelBufSize !== frame.width * frame.height) {
// create a pixel buffer if not yet created or if current frame size is different from previous
pixelBuf = new Uint8Array(frame.width * frame.height);
pixelBufSize = frame.width * frame.height;
}
lzwDecode(st.data[st.pos++], st.readSubBlocksB()); // decode the pixels
if (bitField & 0b1000000) {
// de interlace if needed
frame.interlaced = true;
deinterlace(frame.width);
} else {
frame.interlaced = false;
}
processFrame(frame); // convert to canvas image
}
function processFrame(frame) {
// creates a RGBA canvas image from the indexed pixel data.
var ct, cData, dat, pixCount, ind, useT, i, pixel, pDat, col, frame, ti;
frame.image = document.createElement("canvas");
frame.image.width = gif.width;
frame.image.height = gif.height;
frame.image.ctx = frame.image.getContext("2d");
ct = frame.localColourTableFlag
? frame.localColourTable
: gif.globalColourTable;
if (gif.lastFrame === null) {
gif.lastFrame = frame;
}
useT =
gif.lastFrame.disposalMethod === 2 ||
gif.lastFrame.disposalMethod === 3
? true
: false;
if (!useT) {
frame.image.ctx.drawImage(
gif.lastFrame.image,
0,
0,
gif.width,
gif.height
);
}
cData = frame.image.ctx.getImageData(
frame.leftPos,
frame.topPos,
frame.width,
frame.height
);
ti = frame.transparencyIndex;
dat = cData.data;
if (frame.interlaced) {
pDat = deinterlaceBuf;
} else {
pDat = pixelBuf;
}
pixCount = pDat.length;
ind = 0;
for (i = 0; i < pixCount; i++) {
pixel = pDat[i];
col = ct[pixel];
if (ti !== pixel) {
dat[ind++] = col[0];
dat[ind++] = col[1];
dat[ind++] = col[2];
dat[ind++] = 255; // Opaque.
} else if (useT) {
dat[ind + 3] = 0; // Transparent.
ind += 4;
} else {
ind += 4;
}
}
frame.image.ctx.putImageData(cData, frame.leftPos, frame.topPos);
gif.lastFrame = frame;
if (!gif.waitTillDone && typeof gif.onload === "function") {
doOnloadEvent();
} // if !waitTillDone the call onload now after first frame is loaded
}
// **NOT** for commercial use.
function finnished() {
// called when the load has completed
gif.loading = false;
gif.frameCount = gif.frames.length;
gif.lastFrame = null;
st = undefined;
gif.complete = true;
gif.disposalMethod = undefined;
gif.transparencyGiven = undefined;
gif.delayTime = undefined;
gif.transparencyIndex = undefined;
gif.waitTillDone = undefined;
pixelBuf = undefined; // dereference pixel buffer
deinterlaceBuf = undefined; // dereference interlace buff (may or may not be used);
pixelBufSize = undefined;
deinterlaceBuf = undefined;
gif.currentFrame = 0;
if (gif.frames.length > 0) {
gif.image = gif.frames[0].image;
}
doOnloadEvent();
if (typeof gif.onloadall === "function") {
gif.onloadall.bind(gif)({ type: "loadall", path: [gif] });
}
if (gif.playOnLoad) {
gif.play();
}
}
function canceled() {
// called if the load has been cancelled
finnished();
if (typeof gif.cancelCallback === "function") {
gif.cancelCallback.bind(gif)({ type: "canceled", path: [gif] });
}
}
function parseExt() {
// parse extended blocks
const blockID = st.data[st.pos++];
if (blockID === GIF_FILE.GCExt) {
parseGCExt();
} else if (blockID === GIF_FILE.COMMENT) {
gif.comment += st.readSubBlocks();
} else if (blockID === GIF_FILE.APPExt) {
parseAppExt();
} else {
if (blockID === GIF_FILE.UNKNOWN) {
st.pos += 13;
} // skip unknow block
st.readSubBlocks();
}
}
function parseBlock() {
// parsing the blocks
if (gif.cancel !== undefined && gif.cancel === true) {
canceled();
return;
}
const blockId = st.data[st.pos++];
if (blockId === GIF_FILE.IMAGE) {
// image block
parseImg();
if (gif.firstFrameOnly) {
finnished();
return;
}
} else if (blockId === GIF_FILE.EOF) {
finnished();
return;
} else {
parseExt();
}
if (typeof gif.onprogress === "function") {
gif.onprogress({
bytesRead: st.pos,
totalBytes: st.data.length,
frame: gif.frames.length,
});
}
setTimeout(parseBlock, 0); // parsing frame async so processes can get some time in.
}
function cancelLoad(callback) {
// cancels the loading. This will cancel the load before the next frame is decoded
if (gif.complete) {
return false;
}
gif.cancelCallback = callback;
gif.cancel = true;
return true;
}
function error(type) {
if (typeof gif.onerror === "function") {
gif.onerror.bind(this)({ type: type, path: [this] });
}
gif.onload = gif.onerror = undefined;
gif.loading = false;
}
function doOnloadEvent() {
// fire onload event if set
gif.currentFrame = 0;
gif.nextFrameAt = gif.lastFrameAt = new Date().valueOf(); // just sets the time now
if (typeof gif.onload === "function") {
gif.onload.bind(gif)({ type: "load", path: [gif] });
}
gif.onerror = gif.onload = undefined;
}
function dataLoaded(data) {
// Data loaded create stream and parse
st = new Stream(data);
parse();
}
function loadGif(filename) {
// starts the load
var ajax = new XMLHttpRequest();
ajax.responseType = "arraybuffer";
ajax.onload = function (e) {
if (e.target.status === 404) {
error("File not found");
} else if (e.target.status >= 200 && e.target.status < 300) {
dataLoaded(ajax.response);
} else {
error("Loading error : " + e.target.status);
}
};
ajax.open("GET", filename, true);
ajax.send();
ajax.onerror = function (e) {
error("File error");
};
this.src = filename;
this.loading = true;
}
function play() {
// starts play if paused
if (!gif.playing) {
gif.paused = false;
gif.playing = true;
playing();
}
}
function pause() {
// stops play
gif.paused = true;
gif.playing = false;
clearTimeout(timerID);
}
function togglePlay() {
if (gif.paused || !gif.playing) {
gif.play();
} else {
gif.pause();
}
}
function seekFrame(frame) {
// seeks to frame number.
clearTimeout(timerID);
gif.currentFrame = frame % gif.frames.length;
if (gif.playing) {
playing();
} else {
gif.image = gif.frames[gif.currentFrame].image;
}
}
function seek(time) {
// time in Seconds // seek to frame that would be displayed at time
clearTimeout(timerID);
if (time < 0) {
time = 0;
}
time *= 1000; // in ms
time %= gif.length;
var frame = 0;
while (
time > gif.frames[frame].time + gif.frames[frame].delay &&
frame < gif.frames.length
) {
frame += 1;
}
gif.currentFrame = frame;
if (gif.playing) {
playing();
} else {
gif.image = gif.frames[gif.currentFrame].image;
}
}
function playing() {
var frame;
if (gif.playSpeed === 0) {
gif.pause();
return;
} else {
if (gif.playSpeed < 0) {
gif.currentFrame -= 1;
if (gif.currentFrame < 0) {
gif.currentFrame = gif.frames.length - 1;
}
frame = gif.currentFrame;
frame -= 1;
if (frame < 0) {
frame = gif.frames.length - 1;
}
} else {
gif.currentFrame += 1;
gif.currentFrame %= gif.frames.length;
}
gif.image = gif.frames[gif.currentFrame].image;
timerID = setTimeout(playing, gif.delay / gif.playSpeed);
}
}
var gif = {
// the gif image object
onload: null, // fire on load. Use waitTillDone = true to have load fire at end or false to fire on first frame
onerror: null, // fires on error
onprogress: null, // fires a load progress event
onloadall: null, // event fires when all frames have loaded and gif is ready
paused: false, // true if paused
playing: false, // true if playing
waitTillDone: true, // If true onload will fire when all frames loaded, if false, onload will fire when first frame has loaded
loading: false, // true if still loading
firstFrameOnly: false, // if true only load the first frame
width: null, // width in pixels
height: null, // height in pixels
frames: [], // array of frames
comment: "", // comments if found in file. Note I remember that some gifs have comments per frame if so this will be all comment concatenated
length: 0, // gif length in ms (1/1000 second)
currentFrame: 0, // current frame.
frameCount: 0, // number of frames
playSpeed: 1, // play speed 1 normal, 2 twice 0.5 half, -1 reverse etc...
delay: 100,
lastFrame: null, // temp hold last frame loaded so you can display the gif as it loads
image: null, // the current image at the currentFrame
playOnLoad: true, // if true starts playback when loaded
// functions
load: loadGif, // call this to load a file
cancel: cancelLoad, // call to stop loading
play: play, // call to start play
pause: pause, // call to pause
seek: seek, // call to seek to time
seekFrame: seekFrame, // call to seek to frame
togglePlay: togglePlay, // call to toggle play and pause state
};
return gif;
};
{
if (!window._updateChain) {
window.update = window.update || ((dt) => { });
window._updateChain = [() => window["update"]()];
_hydra.sandbox.userProps = ["speed", "bpm", "fps"];
_hydra.synth.update = (dt) => {
for (func of window._updateChain) {
func(dt);
}
};
}
}
{
const hS = _hydra.s[0].constructor.prototype;
_hydra.s.forEach((source) => {
source._updateChainIndex = window._updateChain.push(() => { });
});
hS.initGif = function (url, delay, params) {
const self = this;
self.gifCanvas = document.createElement("canvas");
self.gifCtx = self.gifCanvas.getContext("2d");
self.gif = new GIF();
self.gif.load(url);
self.gif.onloadall = () => {
self.gif.delay = delay ? delay : self.gif.frames[0].delay;
self.gifCanvas.width = self.gif.width;
self.gifCanvas.height = self.gif.height;
window._updateChain[self._updateChainIndex] = () => {
try {
self.gifCtx.clearRect(
0,
0,
self.gifCanvas.width,
self.gifCanvas.height
);
self.gifCtx.drawImage(self.gif.image, 0, 0);
} catch (e) {
//console.log(e);
}
};
self.init({ src: self.gifCanvas }, params);
};
};
}
//hydra-gsls
//code glsl on the fly inside Hydra code
//by RITCHSE
//docs: https://github.com/ritchse/hydra-extensions/blob/main/doc/hydra-glsl.md
{
const gS = _hydraScope.osc().constructor.prototype;
const _glslExtension = {
inputArray: () =>
new Array(10)
.fill("i")
.map((x, i) => ({ name: x + i, type: "float", default: 1 })),
nodeCount: 0,
checkCode: function (code) {
code = code.trim();
let lines = code.split(";");
let ll = lines.length - 1;
if (!lines[ll]) {
lines.pop();
ll--;
}
lines[ll] = lines[ll].trim();
lines[ll] =
"\n" +
(lines[ll].substring(0, 6) != "return"
? "return " + lines[ll]
: lines[ll]) +
";";
code = lines.join(";");
return code;
},
getObjAndArgs: function (type, args) {
let inputArray = false;
if (args[0] instanceof Array && args[0][0].constructor === String) {
inputArray = this.inputArray().map((x, i) => {
x.name = args[i] ? args[i][0] : x.name;
return x;
});
inputArray = inputArray.slice(0, args.length);
args = args.map((x) => x[1]);
}
let obj = {
name: "_glsl_ext_NODE_" + this.nodeCount,
type: type,
inputs: inputArray || this.inputArray(),
};
this.nodeCount++;
return [obj, args];
},
glslSource: function (code, ...args) {
let prefix = [
!code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "",
!code.includes("vec2 st") ? "vec2 st = _st;\n" : "",
!code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "",
].join("");
let data = this.getObjAndArgs("src", args);
let obj = data[0];
args = data[1];
obj.glsl = prefix + this.checkCode(code);
_hydra.synth.setFunction(obj);
return globalThis[obj.name](...args);
},
glslColor: function (self, code, ...args) {
let prefix = [
!code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "",
!code.includes("vec4 color") ? "vec4 color = _c0;\n" : "",
].join("");
let data = this.getObjAndArgs("color", args);
let obj = data[0];
args = data[1];
obj.glsl = prefix + this.checkCode(code);
_hydra.synth.setFunction(obj);
return gS[obj.name].bind(self)(...args);
},
glslHsv: function (self, code, ...args) {
code = "vec3 hsv = _rgbToHsv(c0.rgb);\n" + code;
code = code + "\nreturn vec4(_hsvToRgb(hsv),c0.a);";
return this.glslColor(self, code, ...args);
},
glslCoord: function (self, code, ...args) {
let prefix = [
!code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "",
!code.includes("vec2 st") ? "vec2 st = _st;\n" : "",
!code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "",
].join("");
let data = this.getObjAndArgs("coord", args);
let obj = data[0];
args = data[1];
obj.glsl = prefix + this.checkCode(code);
_hydra.synth.setFunction(obj);
return gS[obj.name].bind(self)(...args);
},
glslCombine: function (self, code, texture, ...args) {
let prefix = [
!code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "",
!code.includes("vec4 color0") ? "vec4 color0 = _c0;\n" : "",
!code.includes("vec4 c1") ? "vec4 c1 = _c1;\n" : "",
!code.includes("vec4 color1") ? "vec4 color1 = _c1;\n" : "",
].join("");
let data = this.getObjAndArgs("combine", args);
let obj = data[0];
args = data[1];
args.unshift(texture);
obj.glsl = prefix + this.checkCode(code);
_hydra.synth.setFunction(obj);
return gS[obj.name].bind(self)(...args);
},
glslCombineCoord: function (self, code, texture, ...args) {
let prefix = [
!code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "",
!code.includes("vec4 color") ? "vec4 color = _c0;\n" : "",
!code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "",
!code.includes("vec2 st") ? "vec2 st = _st;\n" : "",
!code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "",
].join("");
let data = this.getObjAndArgs("combineCoord", args);
let obj = data[0];
args = data[1];
args.unshift(texture);
obj.glsl = prefix + this.checkCode(code);
_hydra.synth.setFunction(obj);
return gS[obj.name].bind(self)(...args);
},
};
_hydraScope.glsl = _glslExtension.glslSource.bind(_glslExtension);
gS.glslColor = function (code, ...args) {
return _glslExtension.glslColor(this, code, ...args);
};
gS.glslHsv = function (code, ...args) {
return _glslExtension.glslHsv(this, code, ...args);
};
gS.glslCoord = function (code, ...args) {
return _glslExtension.glslCoord(this, code, ...args);
};
gS.glslCombine = function (code, texture, ...args) {
return _glslExtension.glslCombine(this, code, texture, ...args);
};
gS.glslBlend = gS.glslCombine;
gS.glslCombineCoord = function (code, texture, ...args) {
return _glslExtension.glslCombineCoord(this, code, texture, ...args);
};
gS.glslModulate = gS.glslCombineCoord;
}
function Mouse(canvas) {
this.x = 0;
this.y = 0;
this.ax = 0;
this.ay = 0;
this.cx = 0;
this.cy = 0;
this.rx = 0;
this.ry = 0;
this.crx = 0;
this.cry = 0;
this.posx = 0;
this.posy = 0;
this.cposx = 0;
this.cposy = 0;
this.canvas = canvas;
let self = this;
this.handlePointerMove = function (ev) {
const bound = self.canvas.getBoundingClientRect();
self.x = ev.clientX;
self.y = ev.clientY;
self.ax = ev.pageX;
self.ay = ev.pageY;
self.cx = self.ax - bound.left;
self.cy = self.ay - bound.top;
self.rx = self.x / window.innerWidth;
self.ry = self.y / window.innerHeight;
self.crx = self.cx / bound.width;
self.cry = self.cy / bound.height;
self.posx = -self.x / window.innerWidth + 0.5;
self.posy = -self.y / window.innerHeight + 0.5;
self.cposx = -self.cx / bound.width + 0.5;
self.cposy = -self.cy / bound.height + 0.5;
};
if (window.mouse.handlePointerMove)
window.removeEventListener(
"pointermove",
window.mouse.handlePointerMove
);
window.addEventListener("pointermove", this.handlePointerMove);
}
_hydraScope.mouse = new Mouse(window._hydra.canvas);
{
const oP = _hydra.o[0].constructor.prototype;
oP.fboSettings = Array(2).fill({
mag: "nearest",
min: "nearest",
width: width,
height: height,
format: "rgba",
});
oP.setFbos = function (fbo0, fbo1) {
var colors = fbo1 ? [fbo0, fbo1] : [fbo0, fbo0];
this.fboSettings = colors.map((x, i) => {
return {
...this.fboSettings[i],
width: width,
height: height,
...x,
};
});
this.fbos = this.fboSettings.map((x) =>
this.regl.framebuffer({
color: this.regl.texture(x),
depthStencil: false,
})
);
};
oP.setNearest = function () {
this.setFbos({ mag: "nearest", min: "nearest" });
};
oP.setLinear = function () {
this.setFbos({ mag: "linear", min: "linear" });
};
oP.setRepeat = function () {
this.setFbos({ wrapS: "repeat", wrapT: "repeat" });
};
oP.setClamp = function () {
this.setFbos({ wrapS: "clamp", wrapT: "clamp" });
};
oP.setMirror = function () {
this.setFbos({ wrapS: "mirror", wrapT: "mirror" });
};
_hydraScope.oS = { outputs: window._hydra.o };
_hydraScope.oS.setNearest = function () {
this.outputs.forEach((x) => x.setNearest());
};
_hydraScope.oS.setLinear = function () {
this.outputs.forEach((x) => x.setLinear());
};
_hydraScope.oS.setRepeat = function () {
this.outputs.forEach((x) => x.setWrap());
};
_hydraScope.oS.setClamp = function () {
this.outputs.forEach((x) => x.setClamp());
};
_hydraScope.oS.setMirror = function () {
this.outputs.forEach((x) => x.setMirror());
};
_hydraScope.oS.setFbos = function (_x, y) {
this.outputs.forEach((x) => x.setFbos(_x, y));
};
}
{
const video = document.createElement("video"); video.autoplay = true;
const canvas = _hydra.canvas;
const stream = canvas.captureStream();
video.srcObject = stream;
window.hydraPip = () => video.requestPictureInPicture();
window.hydraPictureInPicture = window.hydraPip;
}
//hydra-pixels
//read pixels from each hydra output
//by RITCHSE
{
const oP = _hydra.o[0].constructor.prototype;
const regl = _hydra.o[0].regl;
regl.attributes.preserveDrawingBuffer = true;
oP.read = function (x = 0, y = 0, w = 1, h = 1) {
return regl.read({
framebuffer: this.fbos[this.pingPongIndex],
x: x,
y: y,
width: w,
height: h,
});
};
oP.readAll = function () {
const fbo = this.fbos[this.pingPongIndex];
return regl.read({
framebuffer: fbo,
x: 0,
y: 0,
width: fbo.width,
height: fbo.height,
});
};
}
{
const canvas = _hydra.canvas;
const scope = (_hydraScope.srcMask = function (tex) {
return _hydraScope.src(tex).mask(shape(4, 1, 0));
});
_hydraScope.srcAbs = function (tex) {
if (!tex.hasOwnProperty("src")) return src(tex);
const w = () => tex.src?.width || tex.src?.videoWidth || 0;
const h = () => tex.src?.height || tex.src?.videoHeight || 0;
return _hydraScope.src(tex).scale(
1,
() => w() / canvas.clientWidth,
() => h() / canvas.clientHeight
);
};
_hydraScope.srcAbsMask = function (tex) {
if (!tex.hasOwnProperty("src")) return src(tex);
const w = () => tex.src?.width || tex.src?.videoWidth || 0;
const h = () => tex.src?.height || tex.src?.videoHeight || 0;
return _hydraScope.srcMask(tex).scale(
1,
() => w() / canvas.clientWidth,
() => h() / canvas.clientHeight
);
};
_hydraScope.srcRel = function (tex) {
if (!tex.hasOwnProperty("src")) return src(tex);
const w = () =>
tex.src?.width
? tex.src?.width / tex.src?.height
: tex.src?.videoWidth
? tex.src?.videoWidth / tex.src?.videoHeight
: 0;
const h = () =>
tex.src?.height
? tex.src?.height / tex.src?.width
: tex.src?.videoHeight
? tex.src?.videoHeight / tex.src?.videoWidth
: 0;
const cw = () => canvas.clientWidth / canvas.clientHeight;
const ch = () => canvas.clientHeight / canvas.clientWidth;
return _hydraScope.src(tex).scale(
1,
() => {
const _cw = cw();
const _w = w();
return _cw > _w ? _w / _cw : 1;
},
() => {
const _ch = ch();
const _h = h();
return _ch > _h ? _h / _ch : 1;
}
);
};
_hydraScope.srcRelMask = function (tex) {
if (!tex.hasOwnProperty("src")) return src(tex);
const w = () =>
tex.src?.width
? tex.src?.width / tex.src?.height
: tex.src?.videoWidth
? tex.src?.videoWidth / tex.src?.videoHeight
: 0;
const h = () =>
tex.src?.height
? tex.src?.height / tex.src?.width
: tex.src?.videoHeight
? tex.src?.videoHeight / tex.src?.videoWidth
: 0;
const cw = () => canvas.clientWidth / canvas.clientHeight;
const ch = () => canvas.clientHeight / canvas.clientWidth;
return _hydraScope.srcMask(tex).scale(
1,
() => {
const _cw = cw();
const _w = w();
return _cw > _w ? _w / _cw : 1;
},
() => {
const _ch = ch();
const _h = h();
return _ch > _h ? _h / _ch : 1;
}
);
};
}
{
const gS = _hydraScope.osc().constructor.prototype;
// https://stackoverflow.com/questions/34127294/
function getSwizzles(coords) {
function combinations(input, length, curstr) {
if (curstr.length == length) return [curstr];
var ret = [];
for (var i = 0; i < input.length; i++) {
ret.push.apply(
ret,
combinations(input, length, curstr + input[i])
);
}
return ret;
}
let ret = combinations(coords, coords.length, "");
ret.splice(ret.indexOf("input"), 1);
return ret;
}
const threeComponents = [].concat(getSwizzles("rgb"), getSwizzles("xyz"));
const fourComponents = [].concat(getSwizzles("rgba"), getSwizzles("xyzw"));
function definePropertyFromMethod(method, newName = "") {
newName = newName ? newName : method;
Object.defineProperty(gS, newName, {
configurable: true,
get() {
return this[method].bind(this)();
},
});
}
threeComponents.forEach((swizzle) => {
const name = "_" + swizzle;
_hydra.synth.setFunction({
name,
type: "color",
inputs: [],
glsl: `return _c0.${swizzle}a;`,
});
definePropertyFromMethod(name, swizzle);
});
fourComponents.forEach((swizzle) => {
const name = "_" + swizzle;
_hydra.synth.setFunction({
name,
type: "color",
inputs: [],
glsl: `return _c0.${swizzle};`,
});
definePropertyFromMethod(name, swizzle);
});
Array.from("rgbaxyzw").forEach((elem) => {
const name = "_swizzle_" + elem;
_hydra.synth.setFunction({
name,
type: "color",
inputs: [],
glsl: `return _c0.${elem + elem + elem}a;`,
});
definePropertyFromMethod(name, elem);
});
}
_hydraScope.srcRelMask = function (tex) {
if (!tex.hasOwnProperty("src")) return src(tex);
const w = () =>
tex.src?.width
? tex.src?.width / tex.src?.height
: tex.src?.videoWidth
? tex.src?.videoWidth / tex.src?.videoHeight
: 0;
const h = () =>
tex.src?.height
? tex.src?.height / tex.src?.width
: tex.src?.videoHeight
? tex.src?.videoHeight / tex.src?.videoWidth
: 0;
const cw = () => _hydra.canvas.clientWidth / _hydra.canvas.clientHeight;
const ch = () => _hydra.canvas.clientHeight / _hydra.canvas.clientWidth;
return _hydraScope
.src(tex)
.mask(shape(4, 1, 0))
.scale(
1,
() => {
const _cw = cw();
const _w = w();
return _cw > _w ? _w / _cw : 1;
},
() => {
const _ch = ch();
const _h = h();
return _ch > _h ? _h / _ch : 1;
}
);
};
{
const Source = _hydra.s[0].constructor;
window.hydraText = {
font: "sans-serif",
fontStyle: "normal",
fontSize: "auto",
textAlign: "center",
fillStyle: "white",
strokeStyle: "white",
lineWidth: "2%",
lineJoin: "miter",
canvasResize: 2,
interpolation: "linear"
};
function createSource() {
const s = new Source({
regl: _hydra.regl,
pb: _hydra.pb,
width: _hydra.width,
height: _hydra.height,
});
return s;
}
function isPercentage(property) {
return String(property).endsWith("%");
}
function getPercentage(property) {
return Number(property.substring(0, property.length - 1)) / 100;
}
const _text = function (str, _config, fill = true, stroke = false, fillAfter = false) {
const s = createSource();
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const lines = str.split("\n");
const longestLine = lines.reduce((a, b) => a.length > b.length ? a : b);
if (typeof _config == "string") _config = { font: _config };
const config = Object.assign({}, hydraText);
Object.assign(config, _config);
const font = config.font;
config.font = undefined;
const fontStyle = config.fontStyle;
config.fontStyle = undefined;
config.textBaseline = "middle";
const fontWithSize = (size) => `${fontStyle} ${size} ${font}`;
Object.assign(ctx, config);
canvas.width = _hydra.width;
ctx.font = fontWithSize("1px");
let padding = _hydra.width / 20;
let textWidth = _hydra.width - padding;
let fontSize = textWidth / ctx.measureText(longestLine).width;
canvas.height = fontSize * 1.4 * lines.length;
if (isPercentage(config.fontSize)) fontSize *= getPercentage(config.fontSize);
else if (config.fontSize != "auto") fontSize = Number(config.fontSize.replace(/[^0-9.,]+/, ''));
if (isPercentage(config.lineWidth)) config.lineWidth = fontSize * getPercentage(config.lineWidth);
config.fontSize = undefined;
fontSize *= config.canvasResize;
canvas.width *= config.canvasResize;
canvas.height *= config.canvasResize;
textWidth *= config.canvasResize;
padding *= config.canvasResize;
config.lineWidth *= config.canvasResize;
config.font = fontWithSize(String(fontSize) + "px");
Object.assign(ctx, config);
let x = 0;
if (ctx.textAlign == "center") x = canvas.width / 2;
else if (ctx.textAlign == "left") x = padding / 2;
else if (ctx.textAlign == "right") x = canvas.width - padding / 2;
lines.forEach((line, i) => {
const y = (canvas.height / (lines.length + 1)) * (i + 1);
if (fill) ctx.fillText(line, x, y, textWidth);
if (stroke) ctx.strokeText(line, x, y, textWidth);
if (fillAfter) ctx.fillText(line, x, y, textWidth);
});
s.init({ src: canvas }, { min: config.interpolation, mag: config.interpolation });
return _hydraScope.srcRelMask(s);
};
_hydraScope.text = function (str, config) {
return _text(str, config);
};
_hydraScope.strokeText = function (str, config) {
return _text(str, config, false, true);
};
_hydraScope.fillStrokeText = function (str, config) {
return _text(str, config, true, true, false);
};
_hydraScope.strokeFillText = function (str, config) {
return _text(str, config, false, true, true);
};
}
{
function filterVecArgs(args) {
let pass = [];
args.forEach((arg) => {
if (typeof arg == "number" || typeof arg == "function")
pass = pass.concat(arg);
else if (arg.constructor.name == "Array") {
if (arg._vec) pass = pass.concat(arg);
else pass = pass.concat([arg]);
}
});
return pass;
}
window.vec4 = function (...args) {
args = filterVecArgs(args);
if (args.length == 1) {
const arg = args[0];
return solid(arg, arg, arg, arg);
} else if (args.length == 4) {
return _hydraScope.solid(...args);
} else {
throw new Error("vec4 should receive 4 elements");
}
};
window.vec3 = function (...args) {
args = filterVecArgs(args);
if (args.length == 1) {
const arg = args[0];
let ret = Array.from({ length: 3 }, () => arg);
ret._vec = true;
return ret;
} else if (args.length == 3) {
args._vec = true;
return args;
} else {
throw new Error("vec3 should receive 3 elements");
}
};
window.vec2 = function (...args) {
args = filterVecArgs(args);
if (args.length == 1) {
const arg = args[0];
let ret = Array.from({ length: 2 }, () => arg);
ret._vec = true;
return ret;
} else if (args.length == 2) {
args._vec = true;
return args;
} else {
throw new Error("vec2 should receive 2 elements");
}
};
}
// prereq
//reset fbos. make sure they use clamp
//code taken from hydra-outputs.js
{
if (!_hydraScope.oS) {
const oP = _hydra.o[0].constructor.prototype;
oP.fboSettings = Array(2).fill({
mag: "nearest",
min: "nearest",
width: width,
height: height,
format: "rgba",
});
oP.setFbos = function (fbo0, fbo1) {
var colors = fbo1 ? [fbo0, fbo1] : [fbo0, fbo0];
this.fboSettings = colors.map((x, i) => {
return {
...this.fboSettings[i],
width: width,
height: height,
...x,
};
});
this.fbos = this.fboSettings.map((x) =>
this.regl.framebuffer({
color: this.regl.texture(x),
depthStencil: false,
})
);
};
oP.setClamp = function () {
this.setFbos({ wrapS: "clamp", wrapT: "clamp" });
};
_hydraScope.oS = { outputs: window._hydra.o };
_hydraScope.oS.setClamp = function () {
this.outputs.forEach((x) => x.setClamp());
};
}
_hydraScope.oS.setClamp();
}
// set all coord functions to no-wrap
[
{
name: "scroll",
type: "coord",
inputs: [
{
type: "float",
name: "scrollX",
default: 0.5,
},
{
type: "float",
name: "scrollY",
default: 0.5,
},
{
type: "float",
name: "speedX",
default: 0,
},
{
type: "float",
name: "speedY",
default: 0,
},
],
glsl: `_st.x += scrollX + time*speedX; _st.y += scrollY + time*speedY; return _st;`,
},
{
name: "scrollX",
type: "coord",
inputs: [
{
type: "float",
name: "scrollX",
default: 0.5,
},
{
type: "float",
name: "speed",
default: 0,
},
],
glsl: `_st.x += scrollX + time*speed; return _st;`,
},
{
name: "modulateScrollX",
type: "combineCoord",
inputs: [
{
type: "float",
name: "scrollX",
default: 0.5,
},
{
type: "float",
name: "speed",
default: 0,
},
],
glsl: `_st.x += _c0.r*scrollX + time*speed; return _st;`,
},
{
name: "scrollY",
type: "coord",
inputs: [
{
type: "float",
name: "scrollY",
default: 0.5,
},
{
type: "float",
name: "speed",
default: 0,
},
],
glsl: `_st.y += scrollY + time*speed; return _st;`,
},
{
name: "modulateScrollY",
type: "combineCoord",
inputs: [
{
type: "float",
name: "scrollY",
default: 0.5,
},
{
type: "float",
name: "speed",
default: 0,
},
],
glsl: ` _st.y += _c0.r*scrollY + time*speed; return _st;`,
},
].forEach((x) => _hydra.synth.setFunction(x));
// hydraWrap
window.hydraWrap = {};
hydraWrap.defaultList = [
{
name: "prev",
type: "src",
inputs: [],
glsl: ` vec4 c0 = texture2D(prevBuffer, wrap(_st));
//c0 *= step(abs(_st.x-0.5),0.5);
//c0 *= step(abs(_st.t-0.5),0.5);
return c0;`,
},
{
name: "src",
type: "src",
inputs: [
{
type: "sampler2D",
name: "tex",
default: NaN,
},
],
glsl: ` vec4 c0 = texture2D(tex, wrap(_st));
//c0 *= step(abs(_st.x-0.5),0.5);
//c0 *= step(abs(_st.t-0.5),0.5);
return c0;`,
},
{
name: "wrap",
type: "coord",
inputs: [],
glsl: `return wrap(_st);`,
},
];
hydraWrap.void = false;
hydraWrap.generateFunctionListFromWrapper = function (wrapper) {
return Array.from(hydraWrap.defaultList).map((_f) => {
let f = Object.assign({}, _f);
f.glsl = f.glsl.replace("wrap(_st)", wrapper);
f.glsl = hydraWrap.void ? f.glsl.replaceAll("//c0", "c0") : f.glsl;
return f;
});
};
hydraWrap.wrappers = {
wrap: "fract(_st)",
nowrap: "_st",
mirror: "-abs(fract(_st/2.0)*2.0-1.0)+1.0",
};
hydraWrap.currentWrapper = hydraWrap.wrappers.wrap;
hydraWrap.setWrap = function () {
hydraWrap.void = false;
hydraWrap.currentWrapper = hydraWrap.wrappers.wrap;
hydraWrap
.generateFunctionListFromWrapper(hydraWrap.wrappers.wrap)
.forEach((x) => _hydra.synth.setFunction(x));
};
hydraWrap.setRepeat = hydraWrap.setWrap;
hydraWrap.setNoWrap = function () {
hydraWrap.void = false;
hydraWrap.currentWrapper = hydraWrap.wrappers.nowrap;
hydraWrap
.generateFunctionListFromWrapper(hydraWrap.wrappers.nowrap)
.forEach((x) => _hydra.synth.setFunction(x));
};
hydraWrap.setClamp = hydraWrap.setNoWrap;
hydraWrap.setMirror = function () {
hydraWrap.void = false;
hydraWrap.currentWrapper = hydraWrap.wrappers.mirror;
hydraWrap
.generateFunctionListFromWrapper(hydraWrap.wrappers.mirror)
.forEach((x) => _hydra.synth.setFunction(x));
};
hydraWrap.setCustom = function (wrapper = "_st") {
hydraWrap.void = false;
hydraWrap.currentWrapper = wrapper;
hydraWrap
.generateFunctionListFromWrapper(wrapper)
.forEach((x) => _hydra.synth.setFunction(x));
};
// setVoid should only be called after setting a wrapping mode
hydraWrap.setVoid = function (to = true) {
hydraWrap.void = to;
hydraWrap
.generateFunctionListFromWrapper(hydraWrap.currentWrapper)
.forEach((x) => _hydra.synth.setFunction(x));
};
hydraWrap.setWrap();
}
// module.exports = loadExtensions;
window.setTimeout(() => loadExtensions(), 1000)
// ANTLIA by Ritchse
// https://github.com/ritchse/hydra-antlia
window.screenRatio = window.innerHeight / window.innerWidth
//basic shapes
window.circle = (s = .3, smooth = .007) =>
solid(1, 1, 1, 1).mask(shape(256, s, smooth).scale(1, screenRatio))
window.square = (s = .25, smooth = 0) =>
solid(1, 1, 1, 1).mask(shape(4, s, smooth).scale(1, screenRatio))
window.rectangle = function (s = .3, ratio = [1, 1], smooth = 0) {
var r;
if (ratio[0] > ratio[1])
r = ratio[1];
else
r = ratio[0];
return solid(1, 1, 1, 1).mask(shape(4, s, smooth).scale(1 / r, screenRatio * ratio[0], ratio[1]));
}
window.triangle = function (s = .3, smooth = .007) {
if (typeof s === 'function')
yoffset = () => (0 - s() / 4);
else
yoffset = (0 - s / 4);
return solid(1, 1, 1, 1).mask(shape(3, s, smooth).rotate(Math.PI).scrollY(yoffset).scale(1, screenRatio));
}
//strips
window.horiz = (s = .3, smooth = .0007) =>
solid(1, 1, 1, 1).mask(shape(2, s, smooth))
window.vert = (s = .3, smooth = .0007) =>
solid(1, 1, 1, 1).mask(shape(2, s, smooth).rotate(Math.PI / 2))
window.leftdiag = (s = .3, smooth = .0007) =>
solid(1, 1, 1, 1).mask(shape(2, s, smooth).rotate(Math.PI / 4))
window.rightdiag = (s = .3, smooth = .0007) =>
solid(1, 1, 1, 1).mask(shape(2, s, smooth).rotate(Math.PI / -4))
//quadrants
window.firstquad = (r=1,g=1,b=1,a=1) =>
solid(r,g,b,a).mask(shape(4, 1, .0007).scale(.5).scroll(.25, .25))
window.secondquad = (r=1,g=1,b=1,a=1) =>
solid(r,g,b,a).mask(shape(4, 1, .0007).scale(.5).scroll(.25, -.25))
window.thirdquad = (r=1,g=1,b=1,a=1) =>
solid(r,g,b,a).mask(shape(4, 1, .0007).scale(.5).scroll(-.25, .25))
window.fourthquad = (r=1,g=1,b=1,a=1) =>
solid(r,g,b,a).mask(shape(4, 1, .0007).scale(.5).scroll(-.25, -.25))
window.quad = function (i = 0, r=1,g=1,b=1,a=1) {
i %= 4
switch (i) {
case 0:
return firstquad(r,g,b,a)
case 1:
return secondquad(r,g,b,a)
case 2:
return thirdquad(r,g,b,a)
case 3:
return fourthquad(r,g,b,a)
}
}
//extra
window.star = function (s = .3, v = 5, smooth = 0.007) {
if (typeof v === 'function')
i = () => (Math.PI * (v() / 2));
else
i = Math.PI * (v / 2);
return solid(1, 1, 1, 1).mask(shape(1, -1, smooth).rotate(Math.PI / 8 * 7).kaleid(v).rotate(i).scale(s, screenRatio));
}
window.grid = (x = 8, y = 4, b = 0.05, smooth = .001) => solid(1, 1, 1, 1).mask(shape(4, 1 - b, smooth).repeat(x, y).invert())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment