mirror of
https://github.com/hydra-synth/hydra.git
synced 2025-12-20 21:59:57 +01:00
added web editor to subfolder of frontend
This commit is contained in:
261
frontend/web-editor/src/randomizer/Mutator.js
Normal file
261
frontend/web-editor/src/randomizer/Mutator.js
Normal file
@@ -0,0 +1,261 @@
|
||||
const {Parser} = require("acorn");
|
||||
const {generate} = require('astring');
|
||||
const { defaultTraveler, attachComments, makeTraveler } = require('astravel');
|
||||
const {UndoStack} = require('./UndoStack.js');
|
||||
const repl = require('../repl.js')
|
||||
const glslTransforms = require('hydra-synth/src/glsl/glsl-functions.js')()
|
||||
|
||||
class Mutator {
|
||||
|
||||
constructor(editor) {
|
||||
this.editor = editor;
|
||||
this.undoStack = new UndoStack();
|
||||
|
||||
this.initialVector = [];
|
||||
|
||||
this.funcTab = {};
|
||||
this.transMap = {};
|
||||
this.scanFuncs();
|
||||
this.dumpDict();
|
||||
}
|
||||
|
||||
dumpList() {
|
||||
let gslTab = glslTransforms;
|
||||
gslTab.forEach (v => {
|
||||
var argList = "";
|
||||
v.inputs.forEach((a) => {
|
||||
if (argList != "") argList += ", ";
|
||||
let argL = a.name + ": " + a.type + " {" + a.default + "}";
|
||||
argList = argList + argL;
|
||||
});
|
||||
// console.log(v.name + " [" + v.type + "] ("+ argList + ")");
|
||||
});
|
||||
}
|
||||
|
||||
scanFuncs() {
|
||||
let gslTab = glslTransforms;
|
||||
gslTab.forEach (f => {
|
||||
this.transMap[f.name] = f;
|
||||
if (this.funcTab[f.type] === undefined) {this.funcTab[f.type] = []}
|
||||
this.funcTab[f.type].push(f);
|
||||
});
|
||||
}
|
||||
|
||||
dumpDict() {
|
||||
for(let tn in this.funcTab)
|
||||
{
|
||||
this.funcTab[tn].forEach(f => {
|
||||
var argList = "";
|
||||
f.inputs.forEach((a) => {
|
||||
if (argList != "") argList += ", ";
|
||||
let argL = a.name + ": " + a.type + " {" + a.default + "}";
|
||||
argList = argList + argL;
|
||||
});
|
||||
//console.log(f.name + " [" + f.type + "] ("+ argList + ")");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mutate(options) {
|
||||
// Get text from CodeMirror.
|
||||
let text = this.editor.cm.getValue();
|
||||
this.undoStack.push({text, lastLitX: this.lastLitX});
|
||||
let needToRun = true;
|
||||
let tryCounter = 5;
|
||||
while (needToRun && tryCounter-- >= 0) {
|
||||
// Parse to AST
|
||||
var comments = [];
|
||||
let ast = Parser.parse(text, {
|
||||
locations: true,
|
||||
onComment: comments}
|
||||
);
|
||||
|
||||
// Modify the AST.
|
||||
this.transform(ast, options);
|
||||
|
||||
// Put the comments back.
|
||||
attachComments(ast, comments);
|
||||
|
||||
// Generate JS from AST and set back into CodeMirror editor.
|
||||
let regen = generate(ast, {comments: true});
|
||||
|
||||
this.editor.cm.setValue(regen);
|
||||
try {
|
||||
// Evaluate the updated expression.
|
||||
repl.eval(regen, (code, error) => {
|
||||
// If we got an error, keep trying something else.
|
||||
if (error) {
|
||||
console.log("Eval error: " + regen);
|
||||
}
|
||||
needToRun = error;
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("Exception caught: " + err);
|
||||
needToRun = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doUndo() {
|
||||
// If the current text is unsaved, save it so we can redo if need be.
|
||||
if (this.undoStack.atTop()) {
|
||||
let text = this.editor.cm.getValue();
|
||||
this.undoStack.push({text, lastLitX: this.lastLitX});
|
||||
}
|
||||
// Then pop-off the info to restore.
|
||||
if (this.undoStack.canUndo()) {
|
||||
let {text, lastLitX} = this.undoStack.undo();
|
||||
this.setText(text);
|
||||
this.lastLitX = lastLitX;
|
||||
}
|
||||
}
|
||||
|
||||
doRedo() {
|
||||
if(this.undoStack.canRedo()) {
|
||||
let {text, lastLitX} = this.undoStack.redo();
|
||||
this.setText(text);
|
||||
this.lastLitX = lastLitX;
|
||||
}
|
||||
}
|
||||
|
||||
setText(text) {
|
||||
this.editor.cm.setValue(text);
|
||||
repl.eval(text, (code, error) => {
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// The options object contains a flag that controls how the
|
||||
// Literal to mutate is determined. If reroll is false, we
|
||||
// pick one at random. If reroll is true, we use the same field
|
||||
// we did last time.
|
||||
transform(ast, options) {
|
||||
// An AST traveler that accumulates a list of Literal nodes.
|
||||
let traveler = makeTraveler({
|
||||
go: function(node, state) {
|
||||
if (node.type === 'Literal') {
|
||||
state.literalTab.push(node);
|
||||
} else if (node.type === 'MemberExpression') {
|
||||
if (node.property && node.property.type === 'Literal') {
|
||||
// numeric array subscripts are ineligable
|
||||
return;
|
||||
}
|
||||
} else if (node.type === 'CallExpression') {
|
||||
if (node.callee && node.callee.property && node.callee.property.name && node.callee.property.name !== 'out') {
|
||||
state.functionTab.push(node);
|
||||
}
|
||||
}
|
||||
// Call the parent's `go` method
|
||||
this.super.go.call(this, node, state);
|
||||
}
|
||||
});
|
||||
|
||||
let state = {};
|
||||
state.literalTab = [];
|
||||
state.functionTab = [];
|
||||
|
||||
traveler.go(ast, state);
|
||||
|
||||
this.litCount = state.literalTab.length;
|
||||
this.funCount = state.functionTab.length;
|
||||
if (this.litCount !== this.initialVector.length) {
|
||||
let nextVect = [];
|
||||
for(let i = 0; i < this.litCount; ++i) {
|
||||
nextVect.push(state.literalTab[i].value);
|
||||
}
|
||||
this.initialVector = nextVect;
|
||||
}
|
||||
if (options.changeTransform) {
|
||||
this.glitchTrans(state, options);
|
||||
}
|
||||
else this.glitchLiteral(state, options);
|
||||
|
||||
}
|
||||
|
||||
glitchLiteral(state, options)
|
||||
{
|
||||
let litx = 0;
|
||||
if (options.reroll) {
|
||||
if (this.lastLitX !== undefined) {
|
||||
litx = this.lastLitX;
|
||||
}
|
||||
} else {
|
||||
litx = Math.floor(Math.random() * this.litCount);
|
||||
this.lastLitX = litx;
|
||||
}
|
||||
|
||||
let modLit = state.literalTab[litx];
|
||||
if (modLit) {
|
||||
// let glitched = this.glitchNumber(modLit.value);
|
||||
let glitched = this.glitchRelToInit(modLit.value, this.initialVector[litx]);
|
||||
let was = modLit.raw;
|
||||
modLit.value = glitched;
|
||||
modLit.raw = "" + glitched;
|
||||
console.log("Literal: " + litx + " changed from: " + was + " to: " + glitched);
|
||||
}
|
||||
}
|
||||
|
||||
glitchNumber(num) {
|
||||
if (num === 0) {
|
||||
num = 1;
|
||||
}
|
||||
let range = num * 2;
|
||||
let rndVal = Math.round(Math.random() * range * 1000) / 1000;
|
||||
return rndVal;
|
||||
}
|
||||
|
||||
glitchRelToInit(num, initVal) {
|
||||
if (initVal === undefined) {
|
||||
return glitchNumber(num);
|
||||
} if (initVal === 0) {
|
||||
initVal = 0.5;
|
||||
}
|
||||
|
||||
let rndVal = Math.round(Math.random() * initVal * 2 * 1000) / 1000;
|
||||
return rndVal;
|
||||
}
|
||||
glitchTrans(state, options)
|
||||
{
|
||||
/*
|
||||
state.functionTab.forEach((f)=>{
|
||||
console.log(f.callee.property.name);
|
||||
});
|
||||
*/
|
||||
let funx = Math.floor(Math.random() * this.funCount);
|
||||
if (state.functionTab[funx] === undefined || state.functionTab[funx].callee === undefined || state.functionTab[funx].callee.property === undefined) {
|
||||
console.log("No valid functionTab for index: " + funx);
|
||||
return;
|
||||
}
|
||||
let oldName = state.functionTab[funx].callee.property.name;
|
||||
|
||||
if (oldName == undefined) {
|
||||
console.log("No name for callee");
|
||||
return;
|
||||
}
|
||||
let ftype = this.transMap[oldName].type;
|
||||
if (ftype == undefined) {
|
||||
console.log("ftype undefined for: " + oldName);
|
||||
return;
|
||||
}
|
||||
let others = this.funcTab[ftype];
|
||||
if (others == undefined) {
|
||||
console.log("no funcTab entry for: " + ftype);
|
||||
return;
|
||||
}
|
||||
let changeX = Math.floor(Math.random() * others.length);
|
||||
let become = others[changeX].name;
|
||||
|
||||
// check blacklisted combinations.
|
||||
if (oldName === "modulate" && become === "modulateScrollX")
|
||||
{
|
||||
console.log("Function: " + funx + " changing from: " + oldName + " can't change to: " + become);
|
||||
return;
|
||||
}
|
||||
|
||||
state.functionTab[funx].callee.property.name = become;
|
||||
console.log("Function: " + funx + " changed from: " + oldName + " to: " + become);
|
||||
}
|
||||
|
||||
} // End of class Mutator.
|
||||
|
||||
module.exports = Mutator
|
||||
Reference in New Issue
Block a user