I have been trying to use Animation nodes to create splines on certain edges, as can be seen in this question. It is working, but I am running into a problem: The splines that are generated by the Splines from Edges node are not connected to each other. Their points overlap, but do not connect. When given a bevel object, there are gaps at their corners, and the shading cannot be smoothed.

enter image description here

I need a way to remove these double points. If they were mesh, I'd use 'Remove Doubles', but such an option does not exist for Splines. And if I convert to mesh, remove doubles, and then re-make the splines from edges (all via nodes), it re-creates the same problem.

Is there a proper solution to this in animation nodes?

  • $\begingroup$ The problem is, spline can't branch, so a vertex that is connected to more that two vertices will cause ambiguity in computations. A possible solution is to divide the mesh into parts that form lines, for each part, topologically sort its vertices and create spline out of it. This of course will not be easy to do and will be computationally expensive. I may try to implement that in the node itself at some point. While I don't have a lot of time now, I will do my best to try to answer your question soon. $\endgroup$
    – Omar Emara
    Feb 13, 2018 at 18:21
  • $\begingroup$ I'm fine if it's mesh and then tubes instead of splines if that is easier. $\endgroup$
    – Ascalon
    Feb 13, 2018 at 20:49
  • $\begingroup$ The same algorithm will still have to be done. So it won't be any easier. $\endgroup$
    – Omar Emara
    Feb 14, 2018 at 13:38
  • $\begingroup$ How about Mesh and then use the Skin modifier? That works on branched vertex chains. $\endgroup$
    – Ascalon
    Feb 14, 2018 at 21:19
  • $\begingroup$ That might work, and will probably give better results. There is some work to be done still. But it is much easier to do that our original approach, will write an answer using this method, and the other when I get the time to research it further. $\endgroup$
    – Omar Emara
    Feb 15, 2018 at 15:26

1 Answer 1


Spline From Branche

Starting from version 2.1 of Animation Nodes, the Splines From Edges node has a new method called Spline Per Branch which minimizes discontinuity by combining the vertices of each branch in a single spline. Below is a comparison between the Splines Per Edge (left) and the Splines Per Branch (right) methods:


Skin Modifier

The author suggested using the skin modifier to solidify the filtered edges from the referenced answer. Most of the work is handled by Blender's skin modifier. This, however, imposes certain difficulties. Each of the following sections will concert with one of those difficulties.

As a study case, we shall use the example from the referenced answer. This example filters the edges by Freestyle Marked Edges such that the node tree only outputs the edges that are marked as freestyle:

Study Case


You might have noticed by now that we can't output the filtered edges directly, this is due to the fact that the skin modifier will also skin individual vertices, that is, vertices that are not connected to any other vertices through edges. Such vertices were introduced because their edges may have been filtered (removed), so we should also filter vertices so as to only keep the vertices that are used by the edge list.

I will first initialize a boolean list of length number of vertices that we will use to filter the vertices where the the corresponding boolean to each vertex is True if it was mentioned in one of the edges and False otherwise. I chose to initialize all the booleans to be False by default, the significance of this choice will become apparent in a moment. The code to generate this list is:

BooleanList.fromValue(False, len(vertices))

Next, I will loop over the filtered edges, set the booleans at the indices they carry to True and reassign the list. After the loop is done, all the elements that are referenced in the edges will be true and we will have our filtered list. The implementation is simple and is very similar to the loops iterator loop in my answer in the referenced question, so head over there if you want more detailed explanations:

Filter Vertices

And you can see that the vertices we have are indeed the ones that are part of edges:

Filtered Vertices

Edge Indices

The foregoing section introduces a problem, since we removed some vertices, the vertices indices changed and subsequently the edge indices are no longer valid or connect two entirely different vertices together. To fix this, we have to find the new index of each vertex after the removal of the vertices. We can find a function that maps the old indices to the new ones by studying what I like to call the Indices Train:

Indices Train

The upper row represents the original vertices, each have its own index. The middle row represents the filtered vertices, where you can see, some of vertices were removed. The last row represents our boolean filter list. If you notice, the indices of some of the vertices changed:

  • Blue: Became 1 instead of 2.
  • Orange: Became 2 instead of 4.
  • Green: Became 3 instead of 5.

I want you to take your time and try to figure a possible formulation for the function that maps the old indices to their new ones. The solution is simple:

Each index is reduced by some integer n where n is the number of removed vertices before that index. For instance, Blue was reduced by 1 because only one vertex was removed before it, that is, the cyan one. Orange and Green were reduced by two because two vertices were removed before it, that is, the cyan and the violent one.

So the problem reduced to finding the number of removed vertices before each vertex. Notice that we have a boolean list that tells us exactly which vertices was removed, considering True to be 0 and False to be 1 (Contrary to the normal boolean representation), if for each element, we sum all the booleans before it, we will get exactly how many vertices was removed. For Blue, we have 0+1 = 1 as expected, for Orange, we have 0+1+0+1=2 as expected, and finally for Green, we have 0+1+0+1+0=2 as expected. We can implement this by using a loop:

Removed vertices

We create an initially zero integer, at each iteration, we reassign it to be itself plus not boolean, the not is to implement the statement "considering True to be 0 and False to be 1". And then we append the integer to an integer list. Take your time to understand why this work.

All we have to do now is to subtract those from the old edge indices to get the new ones:

New Edges

The New Edges loops over the edges, get the corresponding number of removed vertices from the Removed vertices loop, subtract it from its index and create a new edge indices from the result of the subtraction. Ending up with a new valid edge indices list. By outputting the filtered vertices and the new edges, we get the mesh that should be skined:

Output Mesh

If we attempt to add a skin modifier now to get the skined mesh, we will be disappointed; because it will not work, the reason is explained in the following section.

Skin Root

The skin modifier requires one vertex to be a Root. A root is basically a vertex that blender uses as a reference to compute orientations for the output mesh. Upon adding a skin modifier manually, blender automatically adds the vertex at index zero as root, that's why you nearly never have to set the root unless needed. However, Animation Nodes keeps writing a new mesh to the object every execution, and since the skin modifier was already added in some previous execution, those new meshes don't have roots simply because nobody set them, this, in turn, cause the skin modifier to fail. The solution is simple, we just have to set the root to some vertex (A process that may be arbitrary if no fine tuning is required). Well, not really. You see, blender store the skin data in the data.skin_vertices data collection and as it turns out, blender doesn't allow us to create such data collection. So the only way to get this to work is by removing the skin modifier and adding it again, something that was described by zeffi in this question. Another method would be using the bpy.ops.mesh.customdata_skin_add operator which require context overwrite, something that we shouldn't really be doing in Animation Nodes. To remove the modifier and add it again, we can use the script:

#If there is a skin modifier by the name "ANSkin", remove it.
if 'ANSkin' in object.modifiers:
    mod = object.modifiers['ANSkin']

#Add a skin modifier called "ANSkin".
mod = object.modifiers.new(type='SKIN', name='ANSkin')
#Edit the options of the modifier:
mod.use_x_symmetry = False

#Output the input object
outObject = object

If you are not familiar with the script node, please study this first.

We now have a working skin modifier:

Working Skin

One thing we can do is control the radius of each of the edges, which we can do using such script:

for v in object.data.skin_vertices[0].data:
    v.radius = radius, radius

Which when used:

Radius Control

Notice that we can control the radius per vertex, so one can use such script:

for v, r in zip(object.data.skin_vertices[0].data, radii):
    v.radius = r, r

Where radii is a float list with the radii, which is used as a random list, returns:

Radii Control

Notice that we can control the radius as two individual values in each direction, normal and cotangent to the edge. I will let you figure this out yourself.

Blend file for study and practice:

  • $\begingroup$ Looks great, and thank you for including the file. I've started a bounty so I can grant an extra reward. $\endgroup$
    – Ascalon
    Feb 17, 2018 at 23:40
  • 2
    $\begingroup$ @Drudge I added another method. We just developed this new algorithm for you. $\endgroup$
    – Omar Emara
    Jul 26, 2018 at 17:11
  • $\begingroup$ Excellent! I've been studying up and am starting to understand it all better. Thank you! $\endgroup$
    – Ascalon
    Jul 26, 2018 at 18:06
  • $\begingroup$ Thumb up for a lot of work!!! $\endgroup$ Nov 21, 2018 at 12:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .