@ Eek, thanks for the thoughts. Very motivating.
I’ve been thinking more about flowCode. Here are ten examples of how I’m seeing various nodes working:
- illustrates a variable node. the VME IDs would exist as custom parameters on the line object. the VME IDs would describe the properties of that variable, for example: type = string, value = “myString”.
- illustrates ‘code canvases’, which are essentially representations of sets of brackets. these sets of brackets can be of type: function(), rollout(), or just a pair (). I’m sure there are more types. these canvases contain statements and variables, and also help to illustrate…
- variable scope in a very intuitive manner. Variables 1 and 2 are parameters of Function A, variable 3 is in the scope of Function A, variables 4 and 5 are in the scope of Function B, and variable 6 exists in global scope.
Let’s move on to statement nodes, starting with the fileIn() node.
- illustrates the fileIn() nodes VME IDs, consisting of an editText value you can enter, or a pickButton that lets you select a node. When a value or node is entered/selected, the object updates itself with a small triangle to visually indicate that it is linked.
- illustrates a string variable linked to a fileIn() node. I envision extra data being displayed in the viewport when an object is selected, for example clicking on the fileIn node would popup the data “fileIn()” then underneath it the values assigned to it’s parameters… in this case it’s just a string variable, since fileIn() only accepts a single value and it must be a string.
- illustrates running a file from the scripts directory, based on a fileName variable. these nodes are placed within a bracket canvas, and you might notice the “+” node adds the string variable to the getDir node string.
- illustrates a new kind of user-interaction based on this layer of nodal abstraction. here I’m dragging a different variable string into the bracket canvas, and based on proximity (a circle object representing a ‘weld’ tolerance value), max recognizes that I want to replace the variable and prompts me with a yes/no messagebox. now I can drag and drop variables into brackets without copying and pasting, or even typing. Coding with the stylus
Moving on, lets check out if/then/else and case of statements:
- illustrates the VME IDs of an if/then/else node. notice that ‘else’ is controlled by a check box, enabling the user to toggle between if/then and if/then/else states. The outgoing links are labeled A and B, referring to then() and else() respectively.
- illustrates a if(…)then(fileIn(…))else(print …) statement in ‘flowCode’ form. Notice the print node has text input or pickNode VME Ids.
- illustrates a case statement. the bold node is the case node, which evaluates to the connected nodes. there can also be a default node. it has a spinner to set the number of case statements. while this particular concept is functional, it is also inherently limiting and I’m not totally happy with it so far.
I plan on building functional variable nodes within a few days, then move into building basic logic nodes, assignment nodes, and also figuring out how to link nodes in the viewport based on proximity. I’m also planning to update the UI of the VME script to separate UI elements from nodes, so you don’t have a dropDown with 2,000 items. In addition to all that, I’d like for the VMeditor to be able to build a fully functional copy of itself with just a click. This will pave the way for building preset UI elements, with nodes, in groups, so you could save UI elements and code (node sets) as presets.
This will eventually lead to: A C# compiler node. A XML reader node. etc… possibilities are endless… even more far fetched: a Python installer node… or a node that would compile a string into a frozen python binary, allowing the user to visually write python .exes in maxscript. many ideas…
@Swordslayer: Thanks! Impure is inspiring.
I figured out a more elegant way to represent if/then/else, case of, and fileIn() statements. This idea is built on top of the assumption that any set of brackets would be represented in the viewport with a bracket canvas. This also allows me to ‘name’ the rectangles based on what they represent.
This simplifies the visuals a bit. Notice the default statement on the ‘case of’. I think this approach is much closer to representing the actual code. There’s a lot more to be developed.
A couple of fun things:
- To save a script’s UI that you are developing, just save the .max file.
- Since each Custom Attribute stays with the object, you can merge and xref UI elements.
I also had the far-fetched idea of writing a .PSD import node – allowing a user to build the UI as layers in Photoshop. Then based on layer name, example: “PickButton”, the VMeditor could load in the .PSD, parse the layers to UI elements (compare layer string name to library of strings), then load in the appropriate bitmaps (assuming you’ve already sliced up the elements and saved them as images). I think this type of node would let users customize their scripts UI very quickly and professionally. This idea is a variation on the PSDtoCSS web service available here. It’s also similar to how Adobe Encore can import .PSDs to build DVD menus based on layer name. Food for thought.
I think you need to sort out base logic first:
if/then/else
continue/exit
while/for
try/catch/throw
case of
One thing your’ll have to start thinking about is combining different logic, i.e the result of a case statement driving a if statement etc – nodes handle this with a line connecting them.
What you’r actually depicting is actually a circuit diagram i think – with hints of state flow code logic.
@Eek: Very good points. You got me thinking about base logic. Here’s some concepts:
- illustrates an if/then/else statement.
- illustrates a for/do
- illustrates a while/do
- illustrates a try/catch
All these concepts are building off the ‘bracket canvas’ node. The ‘bracket canvas’ really helps to visually show scope. The ‘bracket canvas’ is essentially a code block of empty parentheses.
I also think I can ‘compile’ the nodes using a check on each canvases bounding box, so the nodes don’t need to be linked. Instead, the nodes need only to exist within the bounding box of the bracket canvas. This means the rollout bracket canvas can contain other bracket canvases with additional nodes, and the script can recognize which nodes exist within which brackets, then build the code accordingly.
There are some complications with how to visually show the ‘flow direction’ thru the nodes using canvases that I’ll need to address. Questions such as ‘How does the script know which node is the first node to be evaluated within the canvas?’ and ‘How do you link canvases to canvases?’ and ‘Can a canvas be linked to multiple canvases?”.
An interesting thought: See how the for/do and while/do are structured the same? That means they can easily replace each other – allowing a user the ability to drag and drop for/do nodes on top of while/do nodes. While this may not seem useful at first, it illustrates the modular nature of the nodes.
Here’s an attempt at translating some real world code into flowcode.
For this example, I’m using the updateOffsets function that is a part of the VMeditor’s code.
First, here is the updateOffsets function code:
fn updateOffsets =
(
local myParent = $.parent
local myOffsetX = ((abs(floor(myParent.children[1].pos.x-$.pos.x)))-($.width/2)) as string
local myOffsetZ = ((abs(floor(myParent.children[1].pos.z-$.pos.z)))-($.length/2)) as string
$.objectData.xoffset = myOffsetX
$.objectData.yoffset = myOffsetZ
$.objectData.paramsRollout.xoffsetlabel.text = myOffsetX
$.objectData.paramsRollout.yoffsetlabel.text = myOffsetZ
)
The code could be cleaned up a bit more, but this works for a test. Here is the (rough) flowCode:
Triangles are variables. For now, the double-triangle refers to $.
Squares are bracket canvases (which determine the order of evaluation and scope).
Circles are statements, assignments, and operators.
While it (visually) isn’t as simple as I’d like it to be, it does begin to show that ‘flow nodes’ can be used to represent any piece of code.
Here is an idea: looking at the diagram, I can see a possible way that the 2 larger canvases could be condensed into 1 canvas with different variables being plugged in at different times. So you could describe the process in English as “for this function, perform these tasks with these variables. then perform the same task with the same function again, substituting these variables”. This may be a node that is added into the canvas that gives the canvas those special properties. Looking at the code in this light means the other canvases could be condensed into 1 canvas as well, further simplifying the diagram.
It needs more development, but my intention here is to get these ideas out and in the public, so people can point out where the flaws and problems are. Having only 1 perspective means you miss many potentially elegant solutions, thus the reason for my ‘forum-sourcing’ of this idea. Any input is welcome, speak your mind!
I really liked the idea of using 3d elements to draw the UI. Great job !
The first thing that can be improved in the code is that the repetition of same code for different UI elements can be avoided. Second the need to manually link can be eliminated by auto linking based on whether the UI element lies in the rollout margins (or in BB perhaps).
Other things like, different wirecolors for different elements and auto positioning when created (so that the new one does not overlap the older elements. And giving them names instead of “Rectangle01” etc.
I’ll watch this thread and contribute whenever possible.
A little concern i have is that in exposing maxscript to a new interface you’ve introduced new graphical understanding that will be needed. Crucially you want this to be humanly readable – another reason nodal systems work.
Remember you’re abstracting this to be more easily understood. First example:
Compared with:
Lets look at ICE (very nice):
Houdini:
Houdini really is my cuppa tea – (wish i could have some time to learn it mmm… macbook pro…) Anyway, houdini’s VOPs are amazing – basically there nodal operators which contain VEX’s which are inturn customizable vector expressions.
Eek: Remember you’re abstracting this to be more easily understood.
Very good point. This is my main goal with flowCode. So far, it’s complicating things further. This has inspired me to take a different approach.
Here are more thoughts on the ‘values’. These would be strings, integers, floats, arrays, data types…
These represent nodes better. I think I’ll shelf the triangle idea for now, but keep the bracket canvas idea (squares). So, most everything would be in a circle now, including accessing a node’s properties, operators, and methods.
The ‘ca’ dot represents a custom attribute, the dots afterward would have to be blank as there would be no way to visually describe a custom user-set attribute. For example, not everyone uses ‘objectData’ for the ca name, so it would be futile to create ‘objData’ text inside a circle to serve as a node that represents that particular attribute.
Oormi:
The first thing that can be improved in the code is that the repetition of same code for different UI elements can be avoided. Second the need to manually link can be eliminated by auto linking based on whether the UI element lies in the rollout margins (or in BB perhaps).
Other things like, different wirecolors for different elements and auto positioning when created (so that the new one does not overlap the older elements. And giving them names instead of “Rectangle01” etc.
I’ll watch this thread and contribute whenever possible.
@Oormi – good thoughts! I need to clean up the code a bit more, and also restructure some aspects of it to accommodate writing values and other statements. The code posted so far is the result of a 10 hour coding spree, so it needs around another 300 hours of development before I’d be proud of it. I agree and think that auto-linking is an important feature, along with using the canvas’ boundingBox to determine what nodes belong to what canvas.
Here are my thoughts on color and name: the nodes are being designed with the idea that the user would ultimately have control over their color and name. Therefore, all the nodes would be created in a neutral color (grey), and use other visual means to describe them (in the example above, this would be the text ‘$’, ‘node’, etc…). This allows the user to color-code the nodes how they see fit.
I can see this being useful later on, as the script can turn nodes that produce errors a red wirecolor. This would help the debugging process because you’d be able to see where errors were (in red) at a glance.
With the current VMeditor script, the name of the object you create in the viewport becomes the name of the script’s UI element. So, the first rectangle that is created is named ‘rectangle01’ or something, so that becomes the rollout’s name. If you change the objects name to ‘myRollout’ the rollout updates to reflect that. I thought of storing the name using custom attributes, but that only adds more steps to what the user has to do/learn.
I want this to be intuitive and simple, but also dynamic and powerful – so the design must respect that. I have a feeling that there will be many revisions, so please continue to contribute your thoughts. I’ll consider and implement as many as I can, while still steering the script in the direction of intuitive, simple, dynamic, powerful.
Now I’ve got some coding to do…
The circle idea has been conceptually profitable:
The grey circle represents each node’s weld proximity. Nodes within each other’s weld proximity evaluate left to right as code. This eliminates the need for endless connection lines/arrows inside of single line statements.
Proximity size can be user set, node size can be user set as well. Large nodes could be built to support multiple small nodes branching off. Replacing can now be based on an inner proximity check (does the black circle touch the target black circle? then user wants to replace node, else user wants to link node to target node). The target node’s circles could turn green to indicate that max understands you want to replace this node with the node you are dragging. No need for a queryBox to confirm a yes/no, and no need to link any nodes together… so less steps for the user. Less steps with the same result = better.