New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Algorithm For The Splines From Edges Node. #922
New Algorithm For The Splines From Edges Node. #922
Conversation
| return splines | ||
|
|
||
| def splinesFromEdges(Vector3DList vertices, EdgeIndicesList edges, VirtualDoubleList radii, | ||
| bint isVertexRadius): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would prefer a str input here for isVertexRadius because "how are the radii used?" is not really a yes-no question.
There could be more than two options in theory.
|
|
||
| algorithmItems = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just by convention either this should be called algorithmTypeItems or the property in the node should just be called algorithm.
| cdef int *neighboursAmounts = <int*>PyMem_Malloc(verticesAmount * sizeof(int)) | ||
| memset(neighboursAmounts, 0, verticesAmount * sizeof(int)) | ||
| for i in range(edgesAmount): | ||
| neighboursAmounts[edges.data[i].v1] += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could raise an segmentation fault if there aren't enough vertices.
The node itself should check wether the inpu is valid (max vertex index of an edge is smaller than the number of vertices, like in the original code)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the node itself have error handling then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I think the node should do the check. "cutils" usually should not have to make expensive checks, they can expect the data to valid.
Nodes on the other hand can not expect that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright.
| cdef int verticesAmount = len(vertices) | ||
|
|
||
| # Compute how many neighbour each vertex have. | ||
| cdef int *neighboursAmounts = <int*>PyMem_Malloc(verticesAmount * sizeof(int)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in general it would be nice to use the Lists AN provides instead of doing memory allocations yourself if appropriate. The performance impact should be negilible.
| splineVertices = Vector3DList.__new__(Vector3DList, capacity = verticesAmount) | ||
| splineRadii = FloatList.__new__(FloatList, capacity = verticesAmount) | ||
|
|
||
| splineVertices.append(vertices[nonBipolarVertex]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using the append_LowLevel function here might result in a noticable performance improvement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if it would be better to do:
splineRadii = FloatList.__new__(FloatList, length = verticesAmount, capacity = verticesAmount)
splineRadii[0] = radii.get(nonBipolarVertex)
splineRadii[1] = radii.get(nextVertex)
count = 2
for _ in range():
...
splineRadii[count] = radii.get(nextVertex)
count += 1There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that would be even faster, but not sure if it is worth the extra variable
btw, when you specify the length of a list in the constructor, you don't need to specify the capacity (if it is the same)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could reuse the variable j, then we won't have to create any extra variables. And it will be used in a narrow vertical scope. Should we do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reusing the same variable for completely different things is a very bad practice. Let's not do that :D
I'm not even sure why you need the extra variable if you already have variables for last/next and current vertex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant to use it instead of count, I was responding to your comment:
but not sure if it is worth the extra variable.
But yah, I will no longer reuse variables, will have to edit some stuff in account with this. :D
| filledSpaces[nextVertex] -= 1 | ||
|
|
||
| currentVertex = nonBipolarVertex | ||
| for _ in range(verticesAmount): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a better range(...) can be found so that this does not feel like a very inefficient inner loop?
Also by just looking at the way you use the loop it might be more appropriate to use a while loop instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that the number of iterations won't exceed verticesAmount and is likely to be much less, so I though a for loop with a break condition would be safer here.
What do you mean by a better range(...)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just ment a better upper bound could be used in range(...).
Anyway, looks like a for loop shouldn't be used here at all. Can you try to convert it into a while loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that would be my first while loop
|
|
||
| currentVertex = nonBipolarVertex | ||
| while neighboursAmounts[nextVertex] == 2: | ||
| i = currentVertex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you actually want to change i here? Changing it might interfere with the outer loop
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am temporarily storing the value of currentVertex in it. The variable i--- As an index of the for loop--- is not used after that line, so I though it would be OK to overwrite it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the thing is this: it is not used now but you never know if it will be used in the future. By reusing it you sign up for trouble.
Also if anyone wants to convert the code to c, you can't simply reuse the variable, because you would probably end up in an endless loop or so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, will make sure not to do that again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this form?
while neighboursAmounts[nextVertex] == 2:
if neighbours[neighboursStarts[nextVertex]] == currentVertex:
currentVertex = nextVertex
nextVertex = neighbours[neighboursStarts[nextVertex] + 1]
else:
currentVertex = nextVertex
nextVertex = neighbours[neighboursStarts[nextVertex]]currentVertex = nextVertex will be written twice, but we will avoid creating any temporary variables or extra lines of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think such inline expression will also work. Which one should we use?
currentVertex, nextVertex = nextVertex, neighbours[neighboursStarts[nextVertex] + neighbours[neighboursStarts[nextVertex]] == currentVertex]| start += neighboursAmounts[i] | ||
|
|
||
| # Keep track of how many index is in each group of neighbours at each iteration. | ||
| cdef int *filledSpaces = <int*>PyMem_Malloc(verticesAmount * sizeof(int)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think you should use an IntegerList instead of allocating it yourself.
IntegerList.fromValue(0, length = verticesAmount)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so, in general, we shouldn't use malloc if there is a list alternative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, by using lists the code is safer, we can't forget to free and it is cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything now uses lists.
| # Compute how many neighbour each vertex have. | ||
| cdef IntegerList neighboursAmounts = IntegerList.fromValue(0, length = verticesAmount) | ||
| for i in range(edgesAmount): | ||
| neighboursAmounts[edges.data[i].v1] += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for performance reasons you should use .data when accessing list elements from cython code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was just going to ask you about this. Will do.
| # Compute the indices of neighbouring vertices of each vertex. | ||
| cdef IntegerList neighbours = IntegerList(length = edgesAmount * 2) | ||
| for i in range(edgesAmount): | ||
| neighbours.data[neighboursStarts.data[edges.data[i].v1] + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest extra v1 and v2 variables of type unsigned int to reduce line length and improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it a good programming practice to do:
v1, v2 = edges.data[i].v1, edges.data[i].v2or should I separate them into two lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this case it is fine. You could just check if cython generates efficient code for it by checking the generated c code.
(You can create an annotated html version of the file with this command: cython -a path/to/file.pyx)
| nextVertex = neighbours.data[neighboursStarts.data[nonBipolarVertex] + j] | ||
| if (filledSpaces.data[nextVertex] == neighboursAmounts.data[nextVertex] or | ||
| neighboursAmounts.data[nextVertex] != 2): | ||
| splineVertices = Vector3DList.__new__(Vector3DList, length = verticesAmount) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these lists are wayy to large in many cases. Better use the append_LowLevel function instead of allocating huge arrays for every spline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should the initial capacity of those lists be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just don't give them an initial capacity, or maybe 2.
Co-Authored-By: Jacques Lucke <mail@jlucke.com>
|
|
||
| # Generate Splines. | ||
| cdef list splines = [] | ||
| cdef PolySpline spline |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, my bad.
This new algorithm create as much continuous splines from edges as possible, resulting in more workable output for processing and rendering.