mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-14 16:22:56 +01:00
Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
764
packages/noodl-viewer-react/src/nodes/std-library/states.js
Normal file
764
packages/noodl-viewer-react/src/nodes/std-library/states.js
Normal file
@@ -0,0 +1,764 @@
|
||||
'use strict';
|
||||
|
||||
const { EdgeTriggeredInput } = require('@noodl/runtime');
|
||||
const EaseCurves = require('../../easecurves');
|
||||
const BezierEasing = require('bezier-easing');
|
||||
|
||||
const defaultDuration = 300;
|
||||
const previousStates = {},
|
||||
previousValues = {};
|
||||
|
||||
function setRGBA(result, hex) {
|
||||
if (hex === 'transparent' || !hex) {
|
||||
result[3] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const numComponents = (hex.length - 1) / 2;
|
||||
|
||||
for (let i = 0; i < numComponents; ++i) {
|
||||
const index = 1 + i * 2;
|
||||
result[i] = parseInt(hex.substring(index, index + 2), 16);
|
||||
}
|
||||
}
|
||||
|
||||
function componentToHex(c) {
|
||||
var hex = c.toString(16);
|
||||
return hex.length == 1 ? '0' + hex : hex;
|
||||
}
|
||||
|
||||
function rgbaToHex(rgba) {
|
||||
return '#' + componentToHex(rgba[0]) + componentToHex(rgba[1]) + componentToHex(rgba[2]) + componentToHex(rgba[3]);
|
||||
}
|
||||
|
||||
const StatesNode = {
|
||||
name: 'States',
|
||||
docs: 'https://docs.noodl.net/nodes/utilities/logic/states',
|
||||
shortDesc: 'Define states with values and this node can interpolate between these values when the state is changed.',
|
||||
category: 'Animation',
|
||||
initialize: function () {
|
||||
var _this = this,
|
||||
_internal = this._internal;
|
||||
|
||||
_internal.useTransitions = true;
|
||||
_internal.currentValues = {};
|
||||
_internal.stateParameters = {};
|
||||
_internal.stateParameterTypes = {};
|
||||
_internal.startValues = {};
|
||||
_internal.bezierEaseCurves = {};
|
||||
_internal.transitionFuncs = {};
|
||||
|
||||
_internal.valuesAreInitialised = false;
|
||||
_internal.animation = this.context.timerScheduler.createTimer({
|
||||
duration: defaultDuration,
|
||||
ease: EaseCurves.easeOut,
|
||||
onStart: function () {
|
||||
var values = _internal.values;
|
||||
var startValues = _internal.startValues;
|
||||
var stateValues = _internal.stateParameters;
|
||||
var valueTypes = _internal.stateParameterTypes;
|
||||
var prefix = 'value-' + _internal.state + '-';
|
||||
|
||||
this.targetValues = {};
|
||||
this.startValues = {};
|
||||
this.valueTypes = {};
|
||||
|
||||
for (var v in this.transitionCurves) {
|
||||
// var v = values[i];
|
||||
|
||||
if (valueTypes['type-' + v] === 'number' || valueTypes['type-' + v] === undefined) {
|
||||
this.valueTypes[v] = 'number';
|
||||
this.startValues[v] = startValues[v];
|
||||
this.targetValues[v] = stateValues[prefix + v] || 0;
|
||||
} else if (valueTypes['type-' + v] === 'color') {
|
||||
this.valueTypes[v] = 'color';
|
||||
|
||||
this.startValues[v] = [0, 0, 0, 255];
|
||||
setRGBA(this.startValues[v], _this.context.styles.resolveColor(startValues[v] || '#000000'));
|
||||
|
||||
this.targetValues[v] = [0, 0, 0, 255];
|
||||
setRGBA(this.targetValues[v], _this.context.styles.resolveColor(stateValues[prefix + v] || '#000000'));
|
||||
}
|
||||
}
|
||||
},
|
||||
onRunning: function (t) {
|
||||
var ms = t * this.duration;
|
||||
var currentValues = _internal.currentValues;
|
||||
|
||||
var rgba2 = [0, 0, 0, 255];
|
||||
for (var v in this.transitionCurves) {
|
||||
var c = this.transitionCurves[v];
|
||||
// var v = values[i];
|
||||
|
||||
if (ms < c.delay) currentValues[v] = this.startValues[v];
|
||||
else if (ms >= c.delay + c.dur)
|
||||
currentValues[v] = this.valueTypes[v] === 'color' ? rgbaToHex(this.targetValues[v]) : this.targetValues[v];
|
||||
else {
|
||||
var _t = _internal.transitionFuncs[v].get((ms - c.delay) / c.dur);
|
||||
if (this.valueTypes[v] === 'number') {
|
||||
//convert values to Numers, since they might be strings, which can cause NaN
|
||||
currentValues[v] = EaseCurves.linear(Number(this.startValues[v]), Number(this.targetValues[v]), _t);
|
||||
} else if (this.valueTypes[v] === 'color') {
|
||||
let rgba0 = this.startValues[v];
|
||||
let rgba1 = this.targetValues[v];
|
||||
rgba2[0] = Math.floor(EaseCurves.linear(rgba0[0], rgba1[0], _t));
|
||||
rgba2[1] = Math.floor(EaseCurves.linear(rgba0[1], rgba1[1], _t));
|
||||
rgba2[2] = Math.floor(EaseCurves.linear(rgba0[2], rgba1[2], _t));
|
||||
rgba2[3] = Math.floor(EaseCurves.linear(rgba0[3], rgba1[3], _t));
|
||||
|
||||
currentValues[v] = rgbaToHex(rgba2);
|
||||
}
|
||||
}
|
||||
_this.flagOutputDirty(v);
|
||||
}
|
||||
},
|
||||
onFinish: function () {
|
||||
var port = 'reached-' + _internal.state;
|
||||
if (_this.hasOutput(port)) _this.sendSignalOnOutput(port);
|
||||
}
|
||||
});
|
||||
},
|
||||
getInspectInfo() {
|
||||
return `Current state: ${this._internal.state}`;
|
||||
},
|
||||
inputs: {
|
||||
states: {
|
||||
type: { name: 'stringlist', allowEditOnly: true },
|
||||
displayName: 'States',
|
||||
group: 'States',
|
||||
set: function (value) {
|
||||
this._internal.states = value ? value.split(',') : [];
|
||||
|
||||
// Set the state to first value if no state change is scheduled during
|
||||
// input updates
|
||||
if (this._internal.states.length > 0) {
|
||||
var _this = this;
|
||||
if (!_this._internal.state) this.scheduleGoToState(_this._internal.startState || _this._internal.states[0]);
|
||||
/* this.scheduleAfterInputsHaveUpdated(function () {
|
||||
if (!_this._internal.state) _this.goToState(_this._internal.startState ||_this._internal.states[0]);
|
||||
});*/
|
||||
}
|
||||
}
|
||||
},
|
||||
values: {
|
||||
type: { name: 'stringlist', allowEditOnly: true },
|
||||
displayName: 'Values',
|
||||
group: 'Values',
|
||||
set: function (value) {
|
||||
var internal = this._internal;
|
||||
internal.values = value.split(',');
|
||||
|
||||
// Register output values at this point
|
||||
for (var i in internal.values) {
|
||||
this.registerOutputIfNeeded(internal.values[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
toggle: {
|
||||
group: 'Go to state',
|
||||
displayName: 'Toggle',
|
||||
valueChangedToTrue: function () {
|
||||
var internal = this._internal;
|
||||
var _this = this;
|
||||
|
||||
if (!internal.states) return;
|
||||
|
||||
// Figure out which state to toggle to
|
||||
var idx = internal.states.indexOf(internal.state);
|
||||
var nextIdx = (idx + 1) % internal.states.length;
|
||||
|
||||
// Go to state when all updates have updated
|
||||
//this._internal.scheduledToGoToState = internal.states[nextIdx];
|
||||
this.scheduleGoToState(internal.states[nextIdx]);
|
||||
/* this.scheduleAfterInputsHaveUpdated(function () {
|
||||
_this.goToState(internal.states[nextIdx]);
|
||||
});*/
|
||||
}
|
||||
},
|
||||
useTransitions: {
|
||||
type: 'boolean',
|
||||
displayName: 'Use Transitions',
|
||||
group: 'General',
|
||||
default: true,
|
||||
set: function (value) {
|
||||
var internal = this._internal;
|
||||
internal.useTransitions = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
outputs: {
|
||||
currentState: {
|
||||
type: 'string',
|
||||
displayName: 'State',
|
||||
group: 'Current State',
|
||||
getter: function () {
|
||||
return this._internal.state;
|
||||
}
|
||||
},
|
||||
stateChanged: {
|
||||
type: 'signal',
|
||||
displayName: 'State Changed',
|
||||
group: 'Current State'
|
||||
}
|
||||
},
|
||||
prototypeExtensions: {
|
||||
registerOutputIfNeeded: function (name) {
|
||||
var internal = this._internal;
|
||||
|
||||
if (this.hasOutput(name)) return;
|
||||
|
||||
this.registerOutput(name, {
|
||||
getter: function () {
|
||||
return internal.currentValues[name];
|
||||
}
|
||||
});
|
||||
},
|
||||
registerInputIfNeeded: function (name) {
|
||||
var _this = this;
|
||||
var internal = this._internal;
|
||||
|
||||
if (this.hasInput(name)) return;
|
||||
|
||||
if (name.indexOf('to-') === 0) {
|
||||
// This is a go to state signal input
|
||||
var state = name.substring(3);
|
||||
this.registerInput(name, {
|
||||
set: EdgeTriggeredInput.createSetter({
|
||||
valueChangedToTrue: function () {
|
||||
//this._internal.scheduledToGoToState = state;
|
||||
this.scheduleGoToState(state);
|
||||
//this.scheduleAfterInputsHaveUpdated(function () { _this.goToState(state) });
|
||||
}
|
||||
})
|
||||
});
|
||||
} else if (name === 'startState') {
|
||||
// Note: this is kept for backwards compatability, but this port is no longer part of the dynamic ports def
|
||||
// Other state parameters are stored
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
//this._internal.scheduledToGoToState = value;
|
||||
this._internal.startState = value;
|
||||
this.scheduleGoToState(value);
|
||||
// this.scheduleAfterInputsHaveUpdated(function () { _this.goToState(value); });
|
||||
}
|
||||
});
|
||||
} else if (name === 'currentState') {
|
||||
this.registerInput(name, {
|
||||
set: this.scheduleGoToState.bind(this)
|
||||
});
|
||||
} else if (name.indexOf('type-') === 0) {
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
internal.stateParameterTypes[name] = value;
|
||||
}
|
||||
});
|
||||
} else if (name.indexOf('value-') === 0) {
|
||||
// Other state parameters are stored
|
||||
var parts = name.split('-');
|
||||
var state = parts[1];
|
||||
var valueName = parts[2];
|
||||
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
internal.stateParameters[name] = value;
|
||||
if (internal.state === state) {
|
||||
// If we are at the state, update the current value immediately
|
||||
internal.currentValues[valueName] = value;
|
||||
_this.flagOutputDirty(valueName);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (name.search(/duration-/g) === 0) {
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
internal.stateParameters[name] = value;
|
||||
}
|
||||
});
|
||||
} else if (name.search(/transition/g) === 0) {
|
||||
var state = name.substring(11);
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
internal.stateParameters[name] = value;
|
||||
/* if (value === 'cubicBezier') {
|
||||
this.updateCubicBezierFunction(state); //create a default bezier easing curve
|
||||
}*/
|
||||
}
|
||||
});
|
||||
}
|
||||
/* else if (name.search(/cubicBezierP[1-2][X-Y]-/g) === 0) {
|
||||
var state = name.substring(15);
|
||||
this.registerInput(name, {
|
||||
set: function (value) {
|
||||
internal.stateParameters[name] = value;
|
||||
this.updateCubicBezierFunction(state); //update easing curve
|
||||
}
|
||||
|
||||
});
|
||||
}*/
|
||||
},
|
||||
/* updateCubicBezierFunction: function (state) {
|
||||
var points = [];
|
||||
var internal = this._internal;
|
||||
['1X', '1Y', '2X', '2Y'].forEach(function (p, i) {
|
||||
var value = internal.stateParameters['cubicBezierP' + p + '-' + state] || 0;
|
||||
if (i % 2 === 0) {
|
||||
//X values must equal [0,1]
|
||||
value = Math.min(1, Math.max(0, value));
|
||||
}
|
||||
points.push(value);
|
||||
});
|
||||
var cubicBezierEase = BezierEasing(points);
|
||||
internal.bezierEaseCurves[state] = function (start, end, t) {
|
||||
return EaseCurves.linear(start, end, cubicBezierEase.get(t));
|
||||
};
|
||||
},*/
|
||||
setCurrentState: function (value) {
|
||||
this.scheduleGoToState(value);
|
||||
},
|
||||
jumpToState: function (state) {
|
||||
var internal = this._internal;
|
||||
if (!internal.states) return;
|
||||
if (!state) state = internal.states[0];
|
||||
if (internal.state === state) return;
|
||||
|
||||
internal.animation.stop();
|
||||
|
||||
var prefix = 'value-' + state + '-';
|
||||
for (var i in internal.values) {
|
||||
var v = internal.values[i];
|
||||
|
||||
internal.currentValues[v] = internal.stateParameters[prefix + v] || 0;
|
||||
|
||||
this.flagOutputDirty(v);
|
||||
}
|
||||
|
||||
internal.state = state;
|
||||
//console.log('currentState dirty:' + state);
|
||||
this.flagOutputDirty('currentState');
|
||||
|
||||
if (internal.valuesAreInitialised) {
|
||||
// Do not send state changed on first initial state set
|
||||
//console.log('stateChanged signal', state);
|
||||
this.sendSignalOnOutput('stateChanged');
|
||||
}
|
||||
internal.valuesAreInitialised = true;
|
||||
|
||||
this.updateAtStatePorts();
|
||||
},
|
||||
scheduleGoToState: function (state) {
|
||||
var _this = this;
|
||||
|
||||
this._internal.goToState = state;
|
||||
|
||||
//console.log('set go to state: ' + state)
|
||||
if (this.hasScheduledGoToState) return;
|
||||
this.hasScheduledGoToState = true;
|
||||
this.scheduleAfterInputsHaveUpdated(function () {
|
||||
//console.log('changing state: ' + _this._internal.goToState)
|
||||
_this.hasScheduledGoToState = false;
|
||||
_this.goToState(_this._internal.goToState);
|
||||
});
|
||||
},
|
||||
goToState: function (state) {
|
||||
var internal = this._internal;
|
||||
if (!internal.states) return;
|
||||
if (!state) state = internal.states[0];
|
||||
if (internal.state === state) return;
|
||||
|
||||
//this._internal.scheduledToGoToState = undefined;
|
||||
if (!internal.valuesAreInitialised) {
|
||||
// First time go to state is called, jump to the state
|
||||
this.jumpToState(state);
|
||||
} else {
|
||||
// Copy current values as start values
|
||||
var delay = 0;
|
||||
var dur = 0;
|
||||
var transitionCurves = {};
|
||||
for (var i in internal.values) {
|
||||
var v = internal.values[i];
|
||||
|
||||
internal.startValues[v] = internal.currentValues[v];
|
||||
|
||||
const parameterType = internal.stateParameterTypes['type-' + v];
|
||||
if (parameterType === 'boolean') {
|
||||
// These types don't transition, just set them
|
||||
var _b = internal.stateParameters['value-' + state + '-' + v];
|
||||
internal.currentValues[v] = _b === undefined ? false : !!_b;
|
||||
this.flagOutputDirty(v);
|
||||
} else if (parameterType === 'string' || parameterType === 'textStyle') {
|
||||
// These types don't transition, just set them
|
||||
internal.currentValues[v] = internal.stateParameters['value-' + state + '-' + v];
|
||||
this.flagOutputDirty(v);
|
||||
} else {
|
||||
// Figure out transition curve
|
||||
var transitionCurve = internal.stateParameters['transition-' + state + '-' + v];
|
||||
if (!transitionCurve)
|
||||
transitionCurve = internal.stateParameters['transitiondef-' + state] || {
|
||||
curve: [0.0, 0.0, 0.58, 1.0],
|
||||
dur: 300,
|
||||
delay: 0
|
||||
};
|
||||
|
||||
if ((transitionCurve.dur === 0 && transitionCurve.delay === 0) || !internal.useTransitions) {
|
||||
// Simply set the target value
|
||||
internal.currentValues[v] = internal.stateParameters['value-' + state + '-' + v];
|
||||
this.flagOutputDirty(v);
|
||||
} else {
|
||||
// Calculate total duration and delay
|
||||
internal.transitionFuncs[v] = BezierEasing(transitionCurve.curve);
|
||||
transitionCurves[v] = transitionCurve;
|
||||
delay = Math.min(delay, transitionCurve.delay);
|
||||
dur = Math.max(dur, transitionCurve.dur + transitionCurve.delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup and start animation
|
||||
//var easeCurveName = internal.stateParameters['transition-' + state] || 'easeOut';
|
||||
//internal.animation.ease = easeCurveName === 'cubicBezier' ? internal.bezierEaseCurves[state] : EaseCurves[easeCurveName];
|
||||
//var durationKey = 'duration-' + state;
|
||||
//internal.animation.duration = internal.stateParameters.hasOwnProperty(durationKey) ? internal.stateParameters[durationKey] : defaultDuration;
|
||||
if (dur > 0 || delay > 0) {
|
||||
internal.animation.transitionCurves = transitionCurves;
|
||||
internal.animation.duration = dur;
|
||||
internal.animation.delay = delay;
|
||||
internal.animation.start();
|
||||
}
|
||||
|
||||
internal.state = state;
|
||||
//console.log('currentState dirty:' + state);
|
||||
this.flagOutputDirty('currentState');
|
||||
|
||||
//console.log('stateChanged signal', state);
|
||||
this.sendSignalOnOutput('stateChanged');
|
||||
this.updateAtStatePorts();
|
||||
|
||||
if (dur == 0 && delay == 0) {
|
||||
// Send reached signal if no transition
|
||||
var port = 'reached-' + internal.state;
|
||||
if (this.hasOutput(port)) this.sendSignalOnOutput(port);
|
||||
}
|
||||
}
|
||||
},
|
||||
updateAtStatePorts: function () {
|
||||
var internal = this._internal;
|
||||
var states = internal.states;
|
||||
for (var i in states) {
|
||||
var s = states[i];
|
||||
var port = 'at-' + s;
|
||||
|
||||
internal.currentValues[port] = internal.state === s;
|
||||
if (this.hasOutput(port)) this.flagOutputDirty(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function detectRename(before, after) {
|
||||
if (!before || !after) return;
|
||||
|
||||
if (before.length !== after.length) return; // Must be of same length
|
||||
|
||||
var res = {};
|
||||
for (var i = 0; i < before.length; i++) {
|
||||
if (after.indexOf(before[i]) === -1) {
|
||||
if (res.before) return; // Can only be one from before that is missing
|
||||
res.before = before[i];
|
||||
}
|
||||
|
||||
if (before.indexOf(after[i]) === -1) {
|
||||
if (res.after) return; // Only one can be missing,otherwise we cannot match
|
||||
res.after = after[i];
|
||||
}
|
||||
}
|
||||
|
||||
return res.before && res.after ? res : undefined;
|
||||
}
|
||||
|
||||
function updatePorts(nodeId, parameters, editorConnection) {
|
||||
var states = parameters.states;
|
||||
var values = parameters.values;
|
||||
|
||||
var ports = [];
|
||||
|
||||
// Add value outputs
|
||||
values = values ? values.split(',') : undefined;
|
||||
for (var i in values) {
|
||||
var p = values[i];
|
||||
|
||||
ports.push({
|
||||
type: {
|
||||
name: parameters['type-' + p] || 'number',
|
||||
allowConnectionsOnly: true
|
||||
},
|
||||
plug: 'output',
|
||||
group: 'Values',
|
||||
name: p
|
||||
});
|
||||
|
||||
// Type selector
|
||||
ports.push({
|
||||
type: {
|
||||
name: 'enum',
|
||||
enums: [
|
||||
{ label: 'Number', value: 'number' },
|
||||
{ label: 'String', value: 'string' },
|
||||
{ label: 'Boolean', value: 'boolean' },
|
||||
{ label: 'Color', value: 'color' },
|
||||
{ label: 'Text Style', value: 'textStyle' }
|
||||
],
|
||||
allowEditOnly: true
|
||||
},
|
||||
default: 'number',
|
||||
plug: 'input',
|
||||
group: 'Types',
|
||||
displayName: p,
|
||||
name: 'type-' + p
|
||||
});
|
||||
}
|
||||
|
||||
// Add state value inputs
|
||||
states = states ? states.split(',') : undefined;
|
||||
states &&
|
||||
states.forEach(function (state) {
|
||||
values &&
|
||||
values.forEach(function (value) {
|
||||
ports.push({
|
||||
plug: 'input',
|
||||
type: parameters['type-' + value] || 'number',
|
||||
group: state + ' Values',
|
||||
name: 'value-' + state + '-' + value,
|
||||
displayName: value,
|
||||
editorName: state + '|' + value
|
||||
});
|
||||
});
|
||||
|
||||
// State transition
|
||||
if (values && parameters['useTransitions'] !== false) {
|
||||
ports.push({
|
||||
plug: 'input',
|
||||
type: 'curve',
|
||||
displayName: 'Default',
|
||||
default: { curve: [0.0, 0.0, 0.58, 1.0], dur: 300, delay: 0 },
|
||||
group: state + ' Transitions',
|
||||
name: 'transitiondef-' + state
|
||||
});
|
||||
|
||||
values.forEach(function (value) {
|
||||
if (
|
||||
parameters['type-' + value] === undefined ||
|
||||
parameters['type-' + value] === 'number' ||
|
||||
parameters['type-' + value] === 'color'
|
||||
) {
|
||||
ports.push({
|
||||
plug: 'input',
|
||||
type: { name: 'curve' },
|
||||
default: parameters['transitiondef-' + state] || {
|
||||
curve: [0.0, 0.0, 0.58, 1.0],
|
||||
dur: 300,
|
||||
delay: 0
|
||||
},
|
||||
group: state + ' Transitions',
|
||||
name: 'transition-' + state + '-' + value,
|
||||
displayName: value,
|
||||
editorName: 'Transition ' + state + '|' + value
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* ports.push({
|
||||
plug: 'input',
|
||||
type: {
|
||||
name: 'enum',
|
||||
enums: [
|
||||
{ value: 'easeOut', label: "Ease Out" },
|
||||
{ value: 'easeIn', label: "Ease In" },
|
||||
{ value: 'linear', label: "Linear" },
|
||||
{ value: 'easeInOut', label: "Ease In Out" },
|
||||
{ value: 'cubicBezier', label: 'Cubic Bezier' }
|
||||
]
|
||||
},
|
||||
default: 'easeOut',
|
||||
displayName: "Easing Curve",
|
||||
group: state + ' Transition',
|
||||
name: 'transition-' + state,
|
||||
});
|
||||
|
||||
//add cubic bezier inputs if transition is set to cubic
|
||||
if (parameters['transition-' + state] === 'cubicBezier') {
|
||||
ports = ports.concat([
|
||||
{
|
||||
name: 'cubicBezierP1X-' + state,
|
||||
editorName: state + '|' + 'P1 X',
|
||||
displayName: 'P1 X',
|
||||
group: state + ' Transition',
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
default: 0
|
||||
},
|
||||
{
|
||||
name: 'cubicBezierP1Y-' + state,
|
||||
editorName: state + '|' + 'P1 Y',
|
||||
displayName: 'P1 Y',
|
||||
group: state + ' Transition',
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
default: 0
|
||||
},
|
||||
{
|
||||
name: 'cubicBezierP2X-' + state,
|
||||
editorName: state + '|' + 'P2 X',
|
||||
displayName: 'P2 X',
|
||||
group: state + ' Transition',
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
default: 0
|
||||
},
|
||||
{
|
||||
name: 'cubicBezierP2Y-' + state,
|
||||
editorName: state + '|' + 'P2 Y',
|
||||
displayName: 'P2 Y',
|
||||
group: state + ' Transition',
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
default: 0
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
ports.push({
|
||||
plug: 'input',
|
||||
type: 'number',
|
||||
default: defaultDuration,
|
||||
displayName: "Duration",
|
||||
group: state + ' Transition',
|
||||
name: 'duration-' + state,
|
||||
});*/
|
||||
|
||||
// Go to state port
|
||||
ports.push({
|
||||
plug: 'input',
|
||||
type: { name: 'signal', allowConnectionsOnly: true },
|
||||
displayName: 'To ' + state,
|
||||
name: 'to-' + state,
|
||||
group: 'Go to state'
|
||||
});
|
||||
|
||||
// At state output
|
||||
ports.push({
|
||||
plug: 'output',
|
||||
type: 'boolean',
|
||||
displayName: 'At ' + state,
|
||||
name: 'at-' + state,
|
||||
group: 'Current state'
|
||||
});
|
||||
|
||||
// Has reached state output
|
||||
ports.push({
|
||||
plug: 'output',
|
||||
type: 'signal',
|
||||
displayName: 'Has Reached ' + state,
|
||||
name: 'reached-' + state,
|
||||
group: 'Current state'
|
||||
});
|
||||
});
|
||||
|
||||
// Add current state port
|
||||
if (states) {
|
||||
ports.push({
|
||||
plug: 'input',
|
||||
type: { name: 'enum', enums: states },
|
||||
group: 'States',
|
||||
displayName: 'State',
|
||||
name: 'currentState',
|
||||
default: parameters['startState'] || states[0] // This is kept for backwards compatability, the startState port does no longer exist
|
||||
});
|
||||
}
|
||||
|
||||
// Detect state and value rename
|
||||
var stateRenamed = detectRename(previousStates[nodeId], states);
|
||||
previousStates[nodeId] = states;
|
||||
|
||||
var valueRenamed = detectRename(previousValues[nodeId], values);
|
||||
previousValues[nodeId] = values;
|
||||
|
||||
let renamed;
|
||||
if (stateRenamed) {
|
||||
renamed = {
|
||||
plug: 'input',
|
||||
before: stateRenamed.before,
|
||||
after: stateRenamed.after,
|
||||
patterns: [
|
||||
'transition-{{*}}',
|
||||
/* 'duration-{{*}}',
|
||||
'cubicBezierP1X-{{*}}',
|
||||
'cubicBezierP2X-{{*}}',
|
||||
'cubicBezierP1Y-{{*}}',
|
||||
'cubicBezierP2Y-{{*}}',*/
|
||||
'to-{{*}}',
|
||||
'at-{{*}}',
|
||||
'reached-{{*}}'
|
||||
]
|
||||
};
|
||||
|
||||
// A state has been renamed
|
||||
values &&
|
||||
values.forEach(function (value) {
|
||||
renamed.patterns.push('value-{{*}}-' + value);
|
||||
});
|
||||
} else if (valueRenamed) {
|
||||
renamed = [
|
||||
{
|
||||
plug: 'output',
|
||||
before: valueRenamed.before,
|
||||
after: valueRenamed.after,
|
||||
patterns: ['{{*}}']
|
||||
},
|
||||
{
|
||||
plug: 'input',
|
||||
before: valueRenamed.before,
|
||||
after: valueRenamed.after,
|
||||
patterns: ['type-{{*}}']
|
||||
},
|
||||
{
|
||||
plug: 'input',
|
||||
before: valueRenamed.before,
|
||||
after: valueRenamed.after,
|
||||
patterns: states
|
||||
? states.map(function (s) {
|
||||
return 'value-' + s + '-' + '{{*}}';
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
editorConnection.sendDynamicPorts(nodeId, ports, { renamed: renamed });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
node: StatesNode,
|
||||
setup: function (context, graphModel) {
|
||||
if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
|
||||
return;
|
||||
}
|
||||
|
||||
graphModel.on('nodeAdded.States', function (node) {
|
||||
if (node.parameters.states) {
|
||||
updatePorts(node.id, node.parameters, context.editorConnection);
|
||||
}
|
||||
node.on('parameterUpdated', function (event) {
|
||||
if (
|
||||
event.name === 'useTransitions' ||
|
||||
event.name === 'states' ||
|
||||
event.name === 'values' ||
|
||||
event.name.startsWith('transition') ||
|
||||
event.name.startsWith('type-')
|
||||
) {
|
||||
updatePorts(node.id, node.parameters, context.editorConnection);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user