JacquesLucke / animation_nodes

Node based visual scripting system designed for motion graphics in Blender.
Other
2.29k stars 342 forks source link

[request] L-systems into the geometry/kdt/bhv node section #563

Closed YorsLosilla closed 4 years ago

YorsLosilla commented 8 years ago

After watching this tutorial on Houdini: https://vimeo.com/169053556

I have thinked. How cool would be that in AN? Just another episode of wishfull thinking. Maybe this is an interesting challenge for o.g.

JoseConseco commented 8 years ago

I was thinking about it too. Lsystem would be more useful for generative art than for animation though. But imagine creating plants in AN!

JacquesLucke commented 8 years ago

looks super interesting! there is a lot of theory behind these Lsystems but I'll investigate more.

YorsLosilla commented 8 years ago

However if we can use L-systems to create splines, there will be no problem to use them for animation trough smapling or by just animating the end bevel factor.

jeske commented 8 years ago

A basic L-system is quite simple. I started a simple Lsystem script in an AN script node, but I've run into some challenges. You can see the current state of my experiment here:

https://dl.dropboxusercontent.com/u/428467/lsystem_experiment.blend

image

First, I don't know how to import the AN data-types into a script node, so the best I've figured out how to output is a list of pairs of vectors. I'd prefer to output a spline list directly, but that requires importing AN datatypes. I know I could put my script into the AN tree and import these datatypes, but I don't know how to quickly reload and iterate on script when it's sitting in the AN tree.

I'm getting a "maximum recursion depth exceeded in comparison" at only 7 Lsystem recursion levels. Where is this coming from? It's possible to exceed this? What about if I make it an AN node, instead of a script? It's possible to code this non-recursively, but it's a pain.

JacquesLucke commented 8 years ago

that already looks very good!

I agree, the basic concept behind LSystems is simple. But as soon as you want to have high performance and rules with probabilities and conditions it gets more complicated.

I get the same recursion error. Not sure why it happens so early, I haven't checked the code yet in detail. This part looks like there is a LOT of recursion: def lsystem_eval(axiom, rules): if len(axiom) == 0: return ""

cur_sym = axiom[0]
if cur_sym in rules:
    return rules[cur_sym] + lsystem_eval(axiom[1:], rules)
else:
    return cur_sym + lsystem_eval(axiom[1:], rules)

I also see that that it is primitive recursion, but the default recursion depth of python is 1000 or so, which is to low if the axiom is longer. In my opinion increasing the recursion depth is the wrong way to go here. Implementing it without recursion is better.

About AN data types: You can simply write from animation_nodes.data_structures.splines.poly_spline import PolySpline. Then use the PolySpline.fromLocations(vectorList) function to create a spline object. The API might change in the future but shouldn't be hard to update.

Of course you can also write a real node. That becomes easier when your node becomes more complex, but you're right. Reloading is harder then. Although in most cases you can just press "F8" to reload your changes. If that does not work you need restart Blender. I wrote more about that here: http://blender.stackexchange.com/questions/52612/reloading-animation-nodes-node-development-workflow/52632#52632

jeske commented 8 years ago

When I use "from animation_nodes.data_structures.splines.poly_spline import PolySpline" in a script referenced by an AN script node, I get this error below. Is it possible you need to do something to inject that module into the namespace of the script execution?

Registered Animation Nodes with 407 modules.
<!> event has invalid window
Traceback (most recent call last):
  File "C:\Users\David\Dropbox\Public\lsystem_experiment.blend\lsystem.AN.node.py", line 6, in <module>
ImportError: No module named 'animation_nodes'

My mistake on the recursion issue. I don't need to use recursion for the simple first pass Lsystem rule substitutions, only for the stack push/pop operators [] during conversion to geometry. I fixed that and now I can use more generations than I'm willing to wait for.

After fixing a rotation bug, I was able to reproduce the chaotic squares from the Houdini video referenced above using (roughly) the same input strings.

image

JacquesLucke commented 8 years ago

looks good.

oh my fault with the Import statement. it works only when the addon folder name is "animation_nodes" but I guess it's called "animation_nodes-master" on your pc. because of that the animation nodes module is injected into the namespace of the script node. The variable animation_nodes contains the reference to the module.

jeske commented 8 years ago

Great. I was able to get access to PolySpline like this in a script...

PolySpline = animation_nodes.data_structures.splines.poly_spline.PolySpline

However, this doesn't work outside of the script context, and I like being able to press "Run Script" just to test syntax. This format works both inside and outside of the script execution context:

import importlib
poly_spline = importlib.import_module("animation_nodes-master.data_structures.splines.poly_spline")

I submitted an update to the AN docs for the script node.

JacquesLucke commented 8 years ago

thx. will check it when I'm at home.

about the code, this is a cleaner way to reference the module:

import sys
animation_nodes = sys.modules["animation_nodes-master"]

maybe you can also change the doc according to that?

jeske commented 8 years ago

To follow my example, it would be:

import sys
animation_nodes = sys.modules["animation_nodes-master"]
PolySpline = animation_nodes.data_structures.splines.poly_spline.PolySpline

Is that cleaner?

That is also going to produce an odd error if the sys.modules line fails. It will just be a key error. AFAIK, importlib is the "proper" way to handle importing modules with names that are invalid syntax.

jeske commented 8 years ago

After some testing, I have a slightly more complex, but possibly better import proposal. We can recommend script authors use this simple form for imports/references to animation nodes data structures:

PolySpline = animation_nodes.data_structures.splines.poly_spline.PolySpline

If you wish to be able to use "run script" to test for syntax, you can use this import block, which will try to define "animation_nodes" outside of the script context.

import sys
if "animation_nodes" not in globals():
    global animation_nodes
    animation_nodes = sys.modules["animation_nodes-master"]

This has the advantage that the actual imports/references (in the form above) will always work during script execution, no matter what the animation_nodes module directory is called.

Thoughts?

jeske commented 8 years ago

I have the code mostly converted to a node in AN (F8 is now my friend), but I'm having one sticky bit of trouble with the Node UI. I'm trying to create a "preset pulldown", so I can put in preset inputs for several existing L-systems.

image

The trouble is, when I try to set the value of an input in code, it doesn't seem to do anything. I made a property pulldown that holds my preset list, and an onUpdate() method that is getting called when it changes. Inside that, I'm doing:

self.inputs[input_name].value = new_value

However, the input field doesn't change to a new value. The only time I can set the value of the input is during the self.newInput() call.

In case it's relevant, you can see all the code here..

https://github.com/jeske/animation_nodes/blob/master/nodes/spline/splines_from_lsystem.py

I'm also trying to figure out how to put up those nice triangle error notifications, in the case of a malformed rule, or max segment overflow.

JacquesLucke commented 8 years ago

just a quick note: when you do this: self.inputs[input_name] the input name has to be the Name not the identifier. This has something to do with how Blenders API works. I chatted with Lukas Toenne about it and he says that this will likely change in Blender 2.8. But we'll see.

Will comment about the code a bit later when I have more time.

jeske commented 8 years ago

I have a first release of the node ready for some testing.. Here is a demo:

https://www.youtube.com/watch?v=Yc03-O3ymDY&feature=youtu.be

And here is the branch containing my node:

https://github.com/jeske/animation_nodes/tree/lsystem-node

Now that I have it working as a node, I'm trying to figure out how to do something useful with it. For example, to achieve the results in the Houdini video.

JacquesLucke commented 8 years ago

Very impressive demo of the node! Thanks for testing how that node could work in AN!

Unfortunally, I have to say that I will not merge your code soon, and I'm not sure if I will merge it at all. Not because it is bad but because I have some more ideas on how we could make it even more versatile (my goal is to go further than what we've seen in other applications). Also I'd like to rewrite this node in cython when everything is setup which will give us very high performance. Implementing Lsystems will be a great test for the new code infrastructure with Cython. Nevertheless, please keep up the great work, it is VERY helpful to see this node working to get more input -> more ideas. Also it will take some more time until the cython setup is done. So feel free to share your node with the community and try to figure out how to do awesome things with it! I don't want to stop you.

Here is why I think that this:

import sys
animation_nodes = sys.modules["animation_nodes-master"]
PolySpline = animation_nodes.data_structures.splines.poly_spline.PolySpline

is better than this:

import importlib
poly_spline = importlib.import_module("animation_nodes-master.data_structures.splines.poly_spline")
import importlib
poly_spline = importlib.import_module("animation_nodes-master.data_structures.splines.poly_spline")
bezier_spline = importlib.import_module("animation_nodes-master.data_structures.splines.bezier_spline")
# ...

I know that most of these reasons don't have much weight but in my opinion it is very important to care about these little things. Because they add up if you don't care. And make the code much more complex, less maintainable, less readable, ...

When I write code I almost always take every single line and try to find a way to make it clearer/easier/shorter/... In my experience this helps a lot and without it AN would not be where it is today. Oh and of course I don't write perfect code in the first place! Very often I read the same code a week, month or even a year later and change it because I found a better way to do the same thing. ('better' means a lot of different things here)

About this:

import sys
if "animation_nodes" not in globals():
    global animation_nodes
    animation_nodes = sys.modules["animation_nodes-master"]

Yes, it is even better in the context of a script node. The line global animation_nodes is not needed here, you only need it in a function. Another "problem" is that the module is not always called animation_nodes-master but that is not important now :)

Hope that wasn't too much for such small things haha, have a nice day, Jacques

jeske commented 8 years ago

If I don't put the global animation_nodes in, I receive an error during AN Node script execution. I don't know why, because I didn't expect it either. Perhaps it's because of the script execution context.

I know the module is not always called animation_nodes-master. This block is not run during AN Script execution, so it's just a convenience block in case the user wants to click "Run Script" in a text editor to check for syntax errors. They can set the name to their own addon directory name and the script will still work for other people as an AN script node. I've updated the docs to include the above.

I'm curious to see if Cython improves usability of generated geometry. Houdini's entire architecture is built around this, and blender's is not.

I don't think the code is ready for a merge, but I also have little interest in working on code which won't be used. In that case I'll stop and wish you good luck.

YorsLosilla commented 7 years ago

Any chances to resque this?

jeske commented 7 years ago

JacquesLucke wasn't interested in my approach, so I stopped working on it. You can see my patch here:

https://github.com/JacquesLucke/animation_nodes/compare/master...jeske:lsystem-node

JacquesLucke commented 7 years ago

Animation Nodes is not ready for it yet. I will come back to it when the time comes.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.