Giter Site home page Giter Site logo

shamansir / rpd Goto Github PK

View Code? Open in Web Editor NEW
436.0 24.0 69.0 18.08 MB

:ok_hand: A Minimal Engine for creating Node-Based Visual Programming User Interfaces

Home Page: http://shamansir.github.io/rpd

HTML 0.27% JavaScript 91.06% CSS 8.66%
kefir reactive-streams reactive-programming generative-art flow-based-programming dataflow-programming graphics-programming visualisation pure-data streams

rpd's Introduction

RPD — Reactive Patch Development v3.x

Important notice

This project has a successor now, named Noodle: https://github.com/shamansir/noodle, which is very close to be finished and released and, of course, have documentation, like RPD has. Noodle is written in PureScript, the very type-safe and pure functional language and so it is much more reliable and shouldn't cause runtime issues most of the time. It still has FFI to/from JavaScript, though. Another benefit — it can easily be used at server side and so the Networks require no rewriting at all, may be it will even get Terminal renderer at some point.

That way I wasn't supporting this project while I was writing the successor, and I am sorry for that. It should be working, but the features/fixes are freezed for the moment.

Official Documentation & Examples: http://shamansir.github.io/rpd

Join the chat at https://gitter.im/shamansir/rpd

Build Status

Releases

Opened Milestones

Closed Milestones

Latest Stable Version: v2.1.3

Version in development: v3.x.

(no semantic versioning was used before v2.0)

NPM

A video of the engine v0.1.0 in action, demonstrates some of its features: [ Watch ].

Watch

[ Play online ]

(NB: Only modern browsers are supported, tested most in Chrome and Safari, no mobile support for now)


Surely visit Documentation, Examples & More...


RPD is a super-minimal plugin-based Vanilla-JS-driven engine for Node-Based User Interfaces, or Flow-Based Programming Intefaces, this concept has a lot of names — namely, the ones like Pure Data, Quartz Composer, Reaktor, NodeBox, VVVV or any Shader/Material Composing View in your favorite 3D Editor.

And when I say minimal, I really mean it. Minimized and gzipped with its standard configuration, it takes no more than 10KB! (*)

(*) Excluding CSS file which usually takes 2-3 KB and Kefir.js requirement, which also usually takes ~10KB, minified and gzipped. Other configurations provided in repository may take from 10 to 20 KB, but users are free to use as many KB as they need.

Moreover, it's built with the help of Reactive programming (thanks to Kefir.js), and this way it allows a programmer to treat and process any data flow as a stream, so:

colorInlet.stream(Kefir.sequentially(500, ['red', 'navy']));

Will send red and navy values every 500ms to a single color-value inlet in order. It's not the only feature you get with streams, of course, see below for much more.

Here are some GIFs in reduced quality, in addition to a video in rather good quality above, to help you decide if it worths to use this engine or not (also please take a look at code examples below!).

Core GIF PD GIF Animatron GIF

The Engine API provides easy ways to program node networks. Or to define a custom node or a channel type. Even node sets (named toolkits) are enormously easy to build!

Let's switch to some simple examples. Detailed stuff is under the links below.

Constructing a network of nodes:

var patch = Rpd.addPatch('Example');

var firstNode = patch.addNode('core/basic', 'Test');
var boolOutlet = firstNode.addOutlet('util/boolean', 'bool', {
    default: true;
});
firstNode.addOutlet('util/number', { default: 1 });
firstNode.addOutlet('util/number');

var secondNode = patch.addNode('core/basic', 'Foo');
var boolInlet = secondNode.addInlet('util/boolean', 'bool');
var numInlet = secondNode.addInlet('util/number', 'num', {
    allow: [ 'util/boolean' ],
    adapt: function(val) { return (val === true) ? 1 : 0 }
});

boolOutlet.connect(boolInlet);
boolOutlet.connect(numInlet);
boolOutlet.send(false);
boolInlet.stream(Kefir.repeatedly(10, [true, false]));

Creating custom node types is very easy:

Rpd.nodetype('util/sum-of-three', {
    name: 'Sum of Three',
    inlets: {
        'a': { type: 'util/number', name: 'A', default: 1 },
        'b': { type: 'util/number', name: 'B' },
        'c': { type: 'util/number', name: 'C', hidden: true }
    },
    outlets: {
        'sum': { type: 'util/number', name: '∑' }
    },
    process: function(inlets) {
        return { 'sum': (inlets.a || 0) + (inlets.b || 0) + (inlets.c || 0) };
    }
});

Even very complex ones:

Rpd.nodetype('pd/play', function() {
    var lastSound;
    return {
        name: 'play',
        inlets: { 'sound': { type: 'pd/t-obj', default: null } },
        tune: function(updates) { return updates.throttle(50); },
        process: function(inlets, inlets_prev) {
            if (inlets_prev.sound) inlets_prev.sound.pause();
            if (inlets.sound) {
                lastSound = inlets.sound;
                inlets.sound.play();
            }
        },
        handle: {
            'node/turn-off': function() {
                if (lastSound) lastSound.pause();
            }
        }
    }
});

Here's the engine code at a glance;

Features

RPD provides following features (though probably I forgot a dozen):

  • User may observe nodes, manipulate nodes, connect inlets and outlets, effect is seen immediately; User may edit values on inlets, see results inside node bodies or additionally configure them there;
  • Network model may be stored in a simple JS File;
  • Developer may build custom node Toolkits to let user re-use them, in a very easy way; And it's not only restricted with configuring inlets and outlets—actually, every aspect of the node or a channel is configurable;
  • Streams provide developer with an unlimited power in sending, queueing, filtering, mapping/reducing/flattening, packing and un-packing any data, basing on time periods or not; every aspect from Reactive Programming may be used to operate data streams;
  • Plugin system allows to easily add renderers (HTML & SVG renderers are provided, Canvas renderer is planned), styles or importers/exporters for specific Toolkits;
  • Styles allow easily and completely change a look of the interface, so your nodes may appear like ones in Blender or like ones in VVVV with just a few changes; 8 different styles are provided out-of-the-box; Also, styles are very easy to extend or create;
  • Renderers do not use any direct style injection except some very minor cases, they only operate CSS classes, and it means you may completely redesign the look of your interface using only CSS;
  • Developer is free to use any helper library (while RPD tries to use only Kefir and nothing else), and it is very easy: i.e. node renderers may easily use jQuery or d3.js;
  • JSON and Plain Text Import/Export, both provided as an example, with the ability to write module which Imports/Exports from/to any format you want;
  • Ability to be injected into any part of a page;
  • Node model may be easily programmed and updated on-the-fly (i.e. while nodes already send some data);
  • Node model has no side-effects in functional meaning, every change or update is provided through event streams, no data is stored or changed (expect construction); plugins, on the other hand, are completely free to use any programming model they prefer, and they are actually written in much more imperative style than the Engine, but yet they do not change the model;
  • It is so easy to code for RPD, I hope community will be able and happy to write new toolkits, renderers and importers and help evolving the engine;
  • PureData, Animatron and more Toolkits as an examples out-of-the-box;
  • Supports Procedures (re-use node sub-trees by name);
  • Smart layouting;

Using

See Setup and Network sections in Official Documentation.

Just as a quick note (detailed descriptions are for the pages mentioned above), RPD is available from NPM since latest versions, so just do:

npm install rpd --no-optional

Or, if you plan to run examples locally from node_modules, omit the --no-optional flag.

Participating

See Participation sections in Official Documentation.

Feel free to fix issues or do Pull Requests!

rpd's People

Contributors

gitter-badger avatar seanhussey avatar shamansir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rpd's Issues

Ability to connect inlets/outlets

It was rather hard to implement in FRP, but it should be tried once more.

The system should be totally Quartz-Composer-like, so:

  • When user drags from outlet, it's always a new link created which should be connected to some inlet or will be removed
  • When user drags from inlet with no link connected, nothing happens
  • When user drags from inlet with a link connected, this link disconnects and may be either removed or connected to another inlet.
  • [Optional] as in PD, ability to remove link when hovering over it

[Optional] Bezier-kind of links

SVG Renderer

Mostly, it will allow to connect inlets/outlets with curves instead of lines. Dunno if I should use paper.js or pure SVG will be ok, as with HTML renderer.

Some function to prepare nodes of specific type with inlets/outlets

Implemented like:

Rpd.nodetype('core/sum-of-three', {
    name: 'Sum of Three',
    boxWidth: 300,
    inlets: {
        'a': { type: 'core/number', name: 'A' },
        'b': { type: 'core/number', name: 'B' },
        'c': { type: 'core/number', name: 'C' }
    },
    outlets: {
        'sum': { type: 'core/number', default: 0, name: '∑' }
    },
    prepare: function(inlets, outlets) {
        inlets['c'].stream(Kefir.repeatedly(3000, [12, 24, 32]).toProperty(0));
    },
    process: function(inlets) {
        return { 'sum': (inlets.a || 0) + (inlets.b || 0) + (inlets.c || 0) };
    },
    render: {
        'html': function(bodyElm, inlets, outlets) {
            bodyElm.innerHTML = bodyElm.textContent =
                    '∑ (' + (inlets.a || '0') + ', '
                          + (inlets.b || '0') + ', '
                          + (inlets.c || '0') + ') = ' + (outlets.sum || '?');
        }
    }
});

Update inlet value on connection.

No matter if inlet sends values periodically or it's value is constant, just when the inlet was connected to outlet, the last value from inlet should be sent to outlet immediately

Hidden inlets, ability to assign some input-component inside node body to send value to the inlet

It's done. May be not so perfectly, but...

Rpd.nodetype('core/sum-of-three-with-body', (function() {
    var sumContent;
    return {
        name: 'Sum of Three',
        boxWidth: 300,
        inlets: {
            'a': { type: 'core/number', name: 'A', default: 1 },
            'b': { type: 'core/number', name: 'B' },
            'c': { type: 'core/number', name: 'C', hidden: true }
        },
        outlets: {
            'sum': { type: 'core/number', default: 0, name: '∑' }
        },
        process: function(inlets) {
            return { 'sum': (inlets.a || 0) + (inlets.b || 0) + (inlets.c || 0) };
        },
        renderfirst: {
            'html': function(bodyElm, event) {
                var cValInput = document.createElement('input');
                cValInput.style.display = 'block';
                cValInput.type = 'number';
                cValInput.min = 0;
                cValInput.max = 10;
                event['inlet/add'].filter(function(inlet) { return inlet.alias == 'c' })
                                  .onValue(function(inlet) {
                    cValInput.value = 0;
                    inlet.receive(0);
                    Kefir.fromEvent(cValInput, 'change').onValue(function() {
                        inlet.receive(cValInput.value);
                    });
                });
                bodyElm.appendChild(cValInput);
                sumContent = document.createElement('span');
                bodyElm.appendChild(sumContent);
            }
        },
        render: {
            'html': function(bodyElm, inlets, outlets) {
                sumContent.innerHTML = sumContent.textContent =
                            '∑ (' + (inlets.a || '0') + ', '
                                  + (inlets.b || '0') + ', '
                                  + (inlets.c || '0') + ') = ' + (outlets.sum || '?');
            }
        }
    }
})());

PD/Play node overlaps sound.

We probably need the ability to get previous values of inlets in the node, if user wants, so previous sound could be stopped.

Colorize toolkits

To visualize differences in toolkits, it's may be will look nice to mix-colorize titles of nodes from these toolkits and in the search (#295)

Import/Export

Own format and/or PD-format should be implemented as example?

Keyboard controls

Move node with Alt+arrow keys, select node with Tab, select socket with arrow keys, create and finish connecting with Space

Improve adding node-body editors

It looks like this now, which is not very good: (I mean subscribing to events)

Rpd.noderenderer('core/sum-of-three-with-body', 'html', (function() {
    var sumContent;
    return {
        first: function(bodyElm, event) {
            var cValInput = document.createElement('input');
            cValInput.style.display = 'block';
            cValInput.type = 'number';
            cValInput.min = 0;
            cValInput.max = 10;
            event['inlet/add'].filter(function(inlet) { return inlet.alias == 'c' })
                              .onValue(function(inlet) {
                                  cValInput.value = 0;
                                  inlet.receive(0);
                                  Kefir.fromEvent(cValInput, 'change').onValue(function() {
                                      inlet.receive(cValInput.value);
                                  });
                              });
            bodyElm.appendChild(cValInput);
            sumContent = document.createElement('span');
            bodyElm.appendChild(sumContent);
        },
        always: function(bodyElm, inlets, outlets) {
            // ...
        }
    };
})());

Layouting

Node d’n’d, Layouting, Saving node positions (in percentage?, 50px-quantificator?)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.