Skip to content

Commit

Permalink
feat(expression): adds expressions (#468)
Browse files Browse the repository at this point in the history
* feat(expression): adds expressions

Adds the expression editor from modV 2

fix #453 fix #415

* feat(expressions): adds time variable to scope

* feat(expressions): adds preset save/restore to expressions state

* feat(expressions): Make textarea fill up the whole space

* revert(expression): reduces textarea size to align with tween textarea

Co-authored-by: Tim Pietrusky <[email protected]>
  • Loading branch information
2xAA and TimPietrusky authored Dec 1, 2020
1 parent 1d66a15 commit ab13872
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 14 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"interactive-shader-format": "github:vcync/interactive-shader-format-js#feat/ImageBitmap",
"lfo-for-modv": "0.0.1",
"lodash.get": "^4.4.2",
"mathjs": "^3.20.2",
"meyda": "^5.0.1",
"mkdirp": "^0.5.1",
"npm": "6.14.6",
Expand Down
128 changes: 128 additions & 0 deletions src/application/worker/store/modules/expressions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import math from "mathjs";
import uuidv4 from "uuid/v4";

const state = {
assignments: {}
};

// getters
const getters = {
getByInputId: state => inputId => {
const assignmentValues = Object.values(state.assignments);

return assignmentValues.find(assignment => assignment.inputId === inputId);
}
};

function compileExpression(expression) {
const scope = { value: 0, time: 0 };

let newFunction;
try {
const node = math.parse(expression, scope);

newFunction = node.compile();
newFunction.eval(scope);
} catch (e) {
throw e;
}

return newFunction;
}

// actions
const actions = {
create({ commit }, { expression = "value", id, inputId }) {
if (!inputId) {
throw new Error("Input ID required");
}

if (expression.trim() === "value") {
return null;
}

const expressionId = id || uuidv4();

const func = compileExpression(expression);

if (!func) {
throw new Error("Unable to compile Expression");
}

const assignment = {
id: expressionId,
inputId,
func,
expression
};

commit("ADD_EXPRESSION", { assignment });

return expressionId;
},

update({ commit }, { id, expression = "value" }) {
if (!id) {
throw new Error("Expression ID required");
}

const existingExpression = state.assignments[id];

if (!existingExpression) {
throw new Error(`Existing expression with ID ${id} not found`);
}

if (expression.trim() === "value") {
commit("REMOVE_EXPRESSION", { id });
return null;
}

const func = compileExpression(expression);

if (!func) {
throw new Error("Unable to compile Expression");
}

existingExpression.func = func;
existingExpression.expression = expression;

commit("ADD_EXPRESSION", { assignment: existingExpression });
return existingExpression.id;
},

remove({ commit }, args) {
commit("REMOVE_EXPRESSION", args);
},

createPresetData() {
return state;
},

async loadPresetData({ dispatch }, data) {
const assignments = Object.values(data.assignments);
for (let i = 0, len = assignments.length; i < len; i++) {
const assignment = assignments[i];

await dispatch("create", assignment);
}
}
};

// mutations
const mutations = {
ADD_EXPRESSION(state, { assignment }) {
state.assignments[assignment.id] = assignment;
},

REMOVE_EXPRESSION(state, { id }) {
delete state.assignments[id];
}
};

export default {
namespaced: true,
state,
getters,
actions,
mutations
};
25 changes: 13 additions & 12 deletions src/application/worker/store/modules/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ const actions = {
{ moduleId, prop, data, group, groupName, writeToSwap }
) {
const moduleName = state.active[moduleId].$moduleName;
const inputId = state.active[moduleId].$props[prop].id;
const propData = state.registered[moduleName].props[prop];
const currentValue = state.active[moduleId][prop];
const { type } = propData;
Expand All @@ -347,18 +348,18 @@ const actions = {

let dataOut = data;

// store.getters['plugins/enabledPlugins']
// .filter(plugin => 'processValue' in plugin.plugin)
// .forEach(plugin => {
// const newValue = plugin.plugin.processValue({
// currentValue: data,
// controlVariable: prop,
// delta: modV.delta,
// moduleName: name
// })

// if (typeof newValue !== 'undefined') dataOut = newValue
// })
const expressionAssignment = store.getters["expressions/getByInputId"](
inputId
);

if (expressionAssignment) {
const scope = {
value: dataOut,
time: Date.now()
};

dataOut = expressionAssignment.func.eval(scope);
}

if (store.state.dataTypes[type] && store.state.dataTypes[type].create) {
dataOut = await store.state.dataTypes[type].create(dataOut);
Expand Down
15 changes: 14 additions & 1 deletion src/components/InputConfig.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@
</c>
</template>
</CollapsibleRow>

<CollapsibleRow>
<template v-slot:label>
Expression
</template>

<template v-slot:body>
<c span="1..">
<Expression :input-id="inputConfig.id" />
</c>
</template>
</CollapsibleRow>
</grid>
</div>
<div v-else>
Expand All @@ -77,13 +89,15 @@
import AudioFeatures from "./InputLinkComponents/AudioFeatures";
import MIDI from "./InputLinkComponents/MIDI";
import Tween from "./InputLinkComponents/Tween";
import Expression from "./InputLinkComponents/Expression";
import CollapsibleRow from "./CollapsibleRow";
export default {
components: {
AudioFeatures,
MIDI,
Tween,
Expression,
CollapsibleRow
},
Expand Down Expand Up @@ -175,7 +189,6 @@ grid.borders > c:not(:last-child):not(:first-child) {

<style>
.input-config input,
.input-config textarea,
.input-config .select {
max-width: 120px !important;
}
Expand Down
84 changes: 84 additions & 0 deletions src/components/InputLinkComponents/Expression.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<grid columns="4" v-infoView="{ title: iVTitle, body: iVBody, id: iVID }">
<c span="1..">
<grid columns="4">
<c span="2+2">
<Textarea v-model="expression" @change="updateExpression" />
</c>
</grid>
</c>
</grid>
</template>

<script>
export default {
props: {
inputId: {
type: String,
required: true
}
},
data() {
return {
iVTitle: "Expression",
iVBody:
"Expressions allow assigned input values to be shaped with JavaScript synax and math. Multiplication, division, custom intensity curves - whatever goes. `value` is the incoming number, want to make everything 3 times as punchy? `value * 3`. Need to soften things a little? `value / 2`. More examples can be found on modV's website under Guides, Expression.",
iVID: "Expression",
expression: "value",
expressionId: null
};
},
created() {
this.restoreExpressionValues();
},
methods: {
async updateExpression() {
const { inputId, expression, expressionId } = this;
if (expressionId) {
this.expressionId = await this.$modV.store.dispatch(
"expressions/update",
{
id: expressionId,
expression
}
);
} else {
this.expressionId = await this.$modV.store.dispatch(
"expressions/create",
{
expression,
inputId
}
);
}
},
restoreExpressionValues(inputId = this.inputId) {
const expressionAssignment = this.$modV.store.getters[
"expressions/getByInputId"
](inputId);
if (expressionAssignment) {
this.expression = expressionAssignment.expression;
this.expressionId = expressionAssignment.id;
} else {
this.expression = "value";
this.expressionId = null;
}
}
},
watch: {
inputId(inputId) {
this.restoreExpressionValues(inputId);
}
}
};
</script>

<style scoped></style>
41 changes: 40 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.0.11.tgz#09a873fbf15ffd8c18c9c2201ccef425c32b8bf1"
integrity sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==

[email protected]:
version "2.0.4"
resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.0.4.tgz#d8e7cfb9652d1e853e723386421c1a0ca7a48373"
integrity sha512-Syl95HpxUTS0QjwNxencZsKukgh1zdS9uXeXX2Us0pHaqBR6kiZZi0AkZ9VpZFwHJyVIUVzI4EumjWdXP3fy6w==

component-emitter@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
Expand Down Expand Up @@ -3689,6 +3694,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231"
integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==

[email protected]:
version "9.0.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-9.0.1.tgz#1cc8b228177da7ab6498c1cc06eb130a290e6e1e"
integrity sha512-2h0iKbJwnImBk4TGk7CG1xadoA0g3LDPlQhQzbZ221zvG0p2YVUedbKIPsOZXKZGx6YmZMJKYOalpCMxSdDqTQ==

decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
Expand Down Expand Up @@ -4403,7 +4413,7 @@ escape-html@~1.0.3:
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=

[email protected]:
[email protected], escape-latex@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1"
integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==
Expand Down Expand Up @@ -5208,6 +5218,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.12.tgz#0526d47c65a5fb4854df78bc77f7bec708d7b8c3"
integrity sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==

[email protected]:
version "4.0.4"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.4.tgz#04e567110718adf7b52974a10434ab4c67a5183e"
integrity sha512-aK/oGatyYLTtXRHjfEsytX5fieeR5H4s8sLorzcT12taFS+dbMZejnvm9gRa8mZAPwci24ucjq9epDyaq5u8Iw==

fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
Expand Down Expand Up @@ -7636,6 +7651,20 @@ mathjs-expression-parser@^1.0.2:
dependencies:
mathjs "^5.2.3"

mathjs@^3.20.2:
version "3.20.2"
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-3.20.2.tgz#3218aebde7de8cb5627c8fe3a1a216bf399ba91d"
integrity sha512-3f6/+uf1cUtIz1rYFz775wekl/UEDSQ3mU6xdxW7qzpvvhc2v28i3UtLsGTRB+u8OqDWoSX6Dz8gehaGFs6tCA==
dependencies:
complex.js "2.0.4"
decimal.js "9.0.1"
escape-latex "^1.0.0"
fraction.js "4.0.4"
javascript-natural-sort "0.7.1"
seed-random "2.2.0"
tiny-emitter "2.0.2"
typed-function "0.10.7"

mathjs@^5.2.3:
version "5.10.3"
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-5.10.3.tgz#e998885f932ea8886db8b40f7f5b199f89b427f1"
Expand Down Expand Up @@ -11687,6 +11716,11 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=

[email protected]:
version "2.0.2"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==

[email protected]:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
Expand Down Expand Up @@ -11875,6 +11909,11 @@ type-is@~1.6.17, type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"

[email protected]:
version "0.10.7"
resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-0.10.7.tgz#f702af7d77a64b61abf86799ff2d74266ebc4477"
integrity sha512-3mlZ5AwRMbLvUKkc8a1TI4RUJUS2H27pmD5q0lHRObgsoWzhDAX01yg82kwSP1FUw922/4Y9ZliIEh0qJZcz+g==

[email protected]:
version "1.1.0"
resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-1.1.0.tgz#ea149706e0fb42aca1791c053a6d94ccd6c4fdcb"
Expand Down

0 comments on commit ab13872

Please sign in to comment.