Hey @mogh,
Yes, a new topic would be better if we move on to debugging your plugin. But I'll briefly answer here.
- That the comb curve flips between 'above' and 'below' is normal and exactly what I talked about before with mathematical vs 'what artists want' tangents. I once spend here some time on explaining this.
- The problem with your screens is a bit that you do not explain what you would consider incorrect. I assume that you understand why the flipping happens and are okay with it and are concerned about the jaggedness of your combs? Without debugging your code this is very hard to evaluate.
C4D can not get more precise than a 0° degree line object ?
Splines are what is often called smooth geometry, i.e., a parametric curve that is mathematically defined and has infinite precision (you can think of it as infinite points). This is opposed by discrete geometry such as a LineObject which has a finite set of data points. The interpolation settings of a spline are the parameters with which the smooth form ('the spline') is brought into a discrete representation ('the line object') for display and other purposes. So, asking if Cinema 4D could discretize with 0° (or less) does not make too much sense, since discretization with 0° means infinite precision, i.e., the smooth form. For practical purposes, 0.1° should be more than enough. And that is probably already more than order of precision more than you actually need, as humans cannot see that level of detail.
When you do not want to only control the discretization over the angle of change (a.k.a. curvature), but also over the distance of points, you can also set a maximum length for the segments. For that you must change the spline interpolation from 'Adaptive' to 'Subdivided'. This way, you can ensure that even in straight areas, the segments do not become too long.
But the general problem is probably that you do not normalize your data. It makes a difference if the last point is 10 units away from the second last or 1000 units. To approximate the mean curvature between two vertices in some discrete geometry, the formula shown below exists; which is a hack compared to more fancy approaches such as this one. It will approximate the mean curvature between two vertices p and q with the normals np and nq:
(nq - np) * (q - p)
c_i = -------------------
|q - p|
For a mesh vertex you do this with all neighboring vertices and average the result. My guess would be that you are missing this exact normalization step (/|q - p|, i.e., the value divided by the Euclidean distance between the two points) in your comb calculation. Because curvature is inherently a smooth property, the 'neighbor' of a point in a smooth curve/geometry is always exactly 1/infinity units away. So, you need to ensure that the influence of a neighbor vertex in the discrete world is weighted by its distance to the current vertex.
For splines you would probably have to do this for the left and right tangent of each vertex and then take the mean value. But curvature discretization is like all computational geometry subjects not super trivial, and there might be extra/expert steps required for splines I am not aware of right now.
Hope this helps,
Ferdinand
Here is a script I wrote a long time ago where I used that 'formula' to approximate the mean curvature for a mesh. This is non-public code, so it has no comments except for me pointing out the formula.
"""Computes the mean curvature of the selected mesh and stores it in a vertex map.
"""
import c4d
def GetNormal(pid, points, polygons, neighbor):
"""
"""
connected = neighbor.GetPointPolys(pid)
vertexNormals = []
for cpoly in [polygons[pid] for pid in connected]:
if pid == cpoly.a:
o, p, q = points[cpoly.d], points[cpoly.a], points[cpoly.b]
elif pid == cpoly.b:
o, p, q = points[cpoly.a], points[cpoly.b], points[cpoly.c]
elif pid == cpoly.c and cpoly.IsTriangle():
o, p, q = points[cpoly.b], points[cpoly.c], points[cpoly.a]
elif pid == cpoly.c and not cpoly.IsTriangle():
o, p, q = points[cpoly.b], points[cpoly.c], points[cpoly.d]
elif pid == cpoly.d:
o, p, q = points[cpoly.c], points[cpoly.d], points[cpoly.a]
vNormal = ~((o-p) % (q-p))
vertexNormals.append(vNormal)
factor = 1./len(vertexNormals)
return ~(sum(vertexNormals) * factor)
def GetCurvature(node):
"""
"""
neighbor = c4d.utils.Neighbor()
neighbor.Init(node)
points = node.GetAllPoints()
polygons = node.GetAllPolygons()
def getNeighborVertices(pid):
ids = [pid]
for i in neighbor.GetPointPolys(pid):
cpoly = polygons[i]
pnts = [cpoly.a, cpoly.b, cpoly.c]
if not cpoly.IsTriangle():
pnts.append(cpoly.d)
fi = cpoly.Find(pid)
cnt = len(pnts) - 1
prev = pnts[fi - 1] if fi > 0 else pnts[-1]
nxt = pnts[fi + 1] if fi < cnt else pnts[0]
if prev not in ids:
ids.append(prev)
if nxt not in ids:
ids.append(nxt)
for i in ids[1:]:
yield i, points[i]
data = []
for ip, p in enumerate(points):
np = GetNormal(ip, points, polygons, neighbor)
temp = []
for iq, q in getNeighborVertices(ip):
nq = GetNormal(iq, points, polygons, neighbor)
# This is just a very cheap
#
# (nq - np) * (q - p)
# c_i = -------------------
# |q - p|
#
temp.append((nq - np) * (q - p) / (q - p).GetLength())
c = sum(temp) * (1. / len(temp)) * .5 + .5
data.append(c)
neighbor.Flush()
return data
def CreateVertexMap(node, data):
"""
"""
tag = c4d.VariableTag(c4d.Tvertexmap, len(data))
tag.SetAllHighlevelData(data)
tag.SetBit(c4d.BIT_ACTIVE)
node.InsertTag(tag)
c4d.EventAdd()
def main():
"""
"""
data = GetCurvature(op)
CreateVertexMap(op, data)
if __name__ == '__main__':
main()