Closed YorsLosilla closed 4 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!
looks super interesting! there is a lot of theory behind these Lsystems but I'll investigate more.
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.
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
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.
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
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.
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.
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.
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?
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.
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?
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.
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.
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.
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.
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")
sys
module than with the importlib
moduleimport 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")
# ...
sys
to load only the module first makes it more reusable.import
statement in the first place. Scanning a long string for this reason takes more mental effort.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
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.
Any chances to resque this?
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
Animation Nodes is not ready for it yet. I will come back to it when the time comes.
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.
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.