Emergence is a pure graphical, pure functional programming language designed to make images. It is implemented in C++ and can load plugins in runtime. The user can define formulas by connecting nodes in an intuitive user interface.
Yes, I know these features exist already in Blender and a bunch of other programs. Initially, the purpose of this program was to make fractals. I had so much fun and learned so much while making it : now you know why it exists.
Here is a link to the source code repository. You'll need Cmake and Qt5.
How It Works
All the nodes are defined in plugins. The most important ones are X, Y and Output nodes defined in the I/O plugin. These nodes bind different values to each pixel depending on their position in the image. The Output node transforms whatever it takes for each pixel into colors.
For example, the X node binds the leftmost column of pixels to the value 0 and binds the rightmost column of pixels to the value 1. For the columns in between, the binding value is interpolated linearly.
Same thing for the Y node. The lines of pixels are bound between 0 and 1 from top to bottom.
Here is how to multiply X and Y. You connect the Multiply node inputs to X and Y, and connect the Output to the Multiply node. These connections can be done in any order.
The graph is executed recursively from left to right. Each node has a function it uses to update its cache value, then this value is used by the other connected nodes. Wait, did I just say "cache value"? Yes, every node knows what type of value it produces and keeps one as a cache, it also knows if it's valid or not. Every pixel has an ID, and every node knows which pixel ID was used last : no need to invalidate all nodes every time the graph is executed.
For now, there are only 4 types of data a node can keep as cache : real, color, boolean and complex. All 4 can be converted to eachother and are defined in the core of the program. The 4 data types are defined in the same union, so no need to realocate the cache every time it gets converted to another data type.
The graphical user interface is implemented using the Qt5 framework. Here are some very basic features I had a lot of fun implementing :
The undo feature is one of the most satisfying things you can implement using only a stack. A stack of commands : each command is a class with virtual undo and redo functions. Every time you do an undoable thing, a command is pushed on the undo stack and its redo function is executed. And every time you undo, the stack is popped and the undo function of the popped command is executed.
Copy/Paste into clipboard
You can select a node by simply clicking on it, or selecting a bunch of nodes at a time using the rubberband. Selected nodes are highlighted. When nodes are selected, you can copy and paste them, even in another Emergence window. Every node can describe itself on an array of bytes in two ways : ASCII and binary. The node first writes its ID (a string), then its x and y coordinates, then eventually, additional information that describes the state of the node.
After that each node saved itself into the array, it also saves the index of each connected input. This is why the order of the nodes in the array is important.
Now that each node saved itself and its connections into an array, this array can be used to replicate the selection into actual new node instances. This method is also used to save and load nodes from files.
The Gradient Manager
One of the plugins that I had the most fun making is the gradient plugin. It provides the gradient node, which takes a number (preferably between 0 and 1) and returns a color depending on its color gradient. A gradient maps numbers between 0 and 1 to colors and defines its palette of colors by interpolating between these numbers.
The gradient manager is used to create and edit gradients :
As shown on the right, the gradient manager is mainly composed of the Qt ColorPicker dialog and a horizontal bar where you can drag and drop colors to create and edit gradients. Each key color is represented by a black line on the gradient, wich can be grabbed, moved or removed from the gradient.
Values less than the smallest key color or greater than the greatest key color are mapped to the smallest and greatest key colors respectively.
Emergence with FLTK
One day I looked back at the project and realized that I could have had more fun and learned a lot more if I didn't use the Qt framework. Qt5 provides a lot of essential features, which could save a lot of time and effort : but this is not what I was looking for. So I started looking for other toolkits that could replace Qt and settled upon FLTK.
So I rewrote every thing again from scratch including :
- The user interface
- The plugin system
- Seperating the internal mechanisms and the ui