Tuesday, August 13, 2013

Programming continued... vol 4

Have to start working now, so maybe last update in some time.

Latest improvements:
  • plugin system :)
  • ops with geometry operations
  • 3D view
  • viewer Op options for style overrides, also enables sorting of viewed table
Plugin system is a must in any program worth it's weight, so with some fiddling and hatchetwork I pulled this off. Program now checks the /plugins directory at startup and loads .dll libraries that comply with Op interface. From app side this works nice and easy, plugin development on the other hand is, in lack of better word, interesting. To make use of my nice knobs or in fact any other functionality in main app, all source files must be also added to plugin project. This gets them to compile but each plugin is as big as the application itself! Each file references functions in other files and makes this whole thing a bloody tangled mess. I believe it could be possible to make plugins that don't need to clone the whole program to work. At least other people can make this work...Clearly my lack of programming skills is showing its face here :)

In addition to ops with tables, new ops that create or modify geometry are now in place. Started coding a simple library of geometric primitives also, so that generic functions could be used on all of them for inserting vertices etc.

3D view is working as a proof of concept at the moment. Nothing special there and no 3D ops yet. Works on OpenGL.

Viewer Op is now officially an Op and so came functionalities. One can do style overrides, enable sorting of data tables and force points and lines to show constant size and width.

Ahaa, almost forgot! ComboBoxes and other knobs can update their content, for example Combo that lets user select a column for some operation now updates its values from input.

Thursday, August 8, 2013

Programming continued... vol 3

Long time ago I crossed my limits of understanding in programming, but still, through luck and nice findings I still keep fiddling with my little project. During last week I have come from Qt's "Elastic nodes" example to something that almost looks like a functional prototype.

Biggest improvements are:
  • major UI overhaul from node-editor to multiple dockable areas that contain DAG view, table view and node properties
  • knob callbacks work! With some pure luck I managed to get the callback thing working
  • knobs can be added to Op with only one line of code in Op::knobs method
  • new Ops: simple sort (column and direction) and SQL queries
  • nodes can be attached to existing edges by dragging
  • keyboard shortcut to connect viewer to selected node
  • properties view shows knobs for added nodes. Node parameters subwindow can be closed and opened again by double-clicking the node and last opened is positioned on top. 
UI is now based on dockable widgets so that layout can be changed. Look is customised with stylesheet file.

Knob callbacks work nicely, the idea how it should work came one morning and I quickly scribbled some code on a piece of paper. It worked without zero modifications! Adding knobs is very similar to how it works with Nuke plugin development. Syntax is like: "String_knob(callback, &value, label);" where callback is a knob_callback pointer, value is Op variable that is associated with knob, and label is knob label. Knobs get added into row layout that takes care of label and knob placement. In addition I created functions (Nuke style again) to modify last added knob. For example ADD_VALUES(callback, "Ascending, Descending"); adds value list to last knob (ComboBox or similar). Knobs show tooltips and whole node subwindow shows help info when you click on ? button :)

SQL queries took some fiddling with my datamodel because at the moment it is built on QStandardItemModel. To make queries I have to push items into sql database (QSQLITE internally), make the query, and then put items back to model. All Select commands work great but for some reason I can't get queries that modify table structure or delete row to work. They execute when they are hardwired into code but through UI these queries just don't work.

Node area got some love also, because I was annoyed by dragging every connection manually. So now nodes can be inserted into existing edges. One thing is yet to do with node editor - ability to resize backdrops. At the moment they show the resizing dot in the corner but resizing is not working.

Monday, August 5, 2013

Programming continued... vol 2

Getting into exciting stuff now :) Added TableOp base class for operations on data tables and also created three basic operations that subclass TableOp: read table (from comma-separated textfile), select rows, append rows. Ops have pure virtual function called "engine" which calls all node inputs (and through node->execute() calls  their ops) and queries for input data. Data is asked by these calls up to input nodes (read or create nodes) and from there it pours down again. When all node's inputs are executed, node can also execute its ops. So node graph evaluation and execution are not really tied together. Graph evaluation serves the purpose of detecting circular dependencies and displaying order of operations.

Next step is to add table view into main window, because this floating piece is not very nice, and to add possibilities to edit node parameters through UI. I think it needs some kind of callback mechanism but I am not sure yet how to approach this and whether the signal-slot stuff works for it. Main problem is that each node must define its own knobs and there must be connection between every instance of node, it's knob values, and instance's variables.

Saturday, August 3, 2013

Programming continued... vol. 1

Continued my coding tries and added some functionality:
  • nodes can be deleted :) with proper edge handling
  • inputs-outputs reconnect automatically after node deletion and follow main inputs ("B", the base)
  • pressing Ctrl button displays edge break dots (similarly to Nuke) which can be clicked to create new dot nodes
  • nodes can be selected with box select and moved together
  • edges are displayed with A, B or similar signs if necessary
  • when node is selected, all input edges get nicely colored
  • when edges are reconnected, graph is evaluated and resulting list is displayed on screen
At first I used QGraphicsView default functions for handling node selection, moving and box selecting but as I built more and more on top of it, I screwed something up. For example, when moving selected nodes, they jumped to strange places. I ended up writing all mouse interaction functions myself. Some things are still broken, for example pressing Ctrl while dragging selectionbox cancels selection but box is not deleted properly.

PS: for some strange reason Photoshop makes printscreen images fuzzy. Paint on the other hand gets them pixel-sharp. Very strange...

Thursday, August 1, 2013

Dusting off my programming skills

After ages of thinking over it, I finally started (again) with the prototype of my magic software project. It is not related to vfx but is heavily influenced by node-based compositing software and uses the dataflow programming approach as the main principle. I use C++ and Qt framework for GUI, because it is very easy to use and has lots of good examples.

I started with implementing some very basic node and edge manipulation where one can move, add and remove nodes and edges. In addition it has simple functions for node graph sorting - in what order must the nodes be executed. Sorting is not based on any certain algorithm (although it most probably matches one). Idea is based on node stack, where sorting starts from bottom (Viewer node). Active node is put to evaluation list (that holds evaluated nodes), gets removed from stack, and all it's inputs get pushed to stack. Topmost node in stack becomes active node, is put to evaluation list, removed from stack, and questioned for inputs. If it has inputs, all of these get pushed to stack and so on. If stack is empty, we have evaluated all nodes. After that we reverse the evaluation list and remove items that are already visited through joining graph branches. It seems to work nicely and logic is easy to understand, and what is more important, it avoids evaluating branches that do not contribute to output.

When creating new edge, it also detects circular dependencies. First it sorts the graph beginning from starting node of new edge and then searches the sorted list for end node. If end node is in that list, it means that there is circular dependency and edge is not created.

I started testing the plugin interface but could not find a good way for creating an arbitrary number of parameter knobs (as specified in plugin) and exchanging the parameters between UI and plugin. I can create widgets that are specified in plugin but don't have a good way to pass parameters back and forth when number and type of parameters is set from inside the plugin.

Little teaser also :)