Hello @bentraje,

Thank you for reaching out to us. Your question is a bit ambiguous, but I largely understand it so that you want to control hair guide vertices programmatically, e.g., bind the vertex of a hair guide to a null object, or even more complex cases as *"splines and alembic"*. The short answer is, no, that is not possible from Python, but it would be from C++.

The longer answer is that while there is c4d.modules.hair in Python, you are effectively restricted to read access. You can write hair guide vertex values, and they will be reflected in a scene in a certain sense, but you lack the means (MSG_HAIR_GET_OBJECT_TYPE) to properly update the hair object using these guides. Which will then result in unrooted hair guides, duplicated guides, and finally crashes. Find an example below.

I will update the Python docs with a warning for others not to run into the same trap I did. What you could however do, is using the `Hair Object > Guides > Editing > Points Link`

parameter to drive the guides. This could be with one of the many deformation tools of Cinema 4D, please reach out to user support for any help with this, or using Python, find a simple example below.

Cheers,

Ferdinand

#### Hair Module in Python

You *should not be doing what I am doing here*, as this will lead to crashes.

Scene File: python_hair_crash.c4d

#### Spline Workaround

Scene File: python_hair_spline_rig.c4d

Result:

Code:

```
"""Demonstrates how to drive hair guides with a null object.
This could all be done much more elaborately, I implemented here the most simple version possible.
"""
import c4d
import typing
doc: c4d.documents.BaseDocument
op: c4d.BaseTag
bt: typing.Optional[c4d.threading.BaseThread]
ID_SPLINE_OBJECT: int = (c4d.ID_USERDATA, 1)
ID_SPLINE_SEGMENT: int = (c4d.ID_USERDATA, 2)
def GetSegmentVertexIndexRange(spline: c4d.SplineObject, index: int) -> tuple[int, int, int]:
"""Returns the vertex index range for a given segment in a spline.
Args:
spline: The spline to evaluate.
index: The segment index to evaluate.
Returns:
The number of vertices in #index,
The first vertex index in #index,
The last vertex index in #index
"""
# Validate the inputs.
if not isinstance(spline, c4d.SplineObject):
raise TypeError(f"{spline = }")
count: int = spline.GetSegmentCount()
if index > (count - 1):
raise IndexError(f"Segment index {index} is out of bounds for {spline}.")
# Get the start and end index by summing up the number of points in all segments up to #index.
a, b = 0, 0
for i in range(count):
if i < index:
a += spline.GetSegment(i)["cnt"]
continue
b = a + spline.GetSegment(i)["cnt"] - 1
break
return b - a, a, b
def main() -> None:
"""
"""
# Get the inputs.
target: c4d.BaseObject = op.GetMain()
if not isinstance(target, c4d.BaseObject):
raise RuntimeError("Tag is not attached to an object.")
spline: c4d.SplineObject = op[ID_SPLINE_OBJECT]
if not isinstance(spline, c4d.SplineObject):
raise TypeError(f"{spline = }")
# Get the vertex index range for the given segment index, e.g., (0, 8) for the first segment or
# (9, 17) for the second segment, for a spline with two segments, each holding eight points.
index: int = op[ID_SPLINE_SEGMENT]
count, a, b = GetSegmentVertexIndexRange(spline, index)
# The position of the null object in the coordinate system of the spline object and all its points.
p: c4d.Vector = ~spline.GetMg() * target.GetMg().off
points: list[c4d.Vector] = spline.GetAllPoints()
# The last point q in the segment and the delta vector from q to our target point p.
q: c4d.Vector = points[b]
delta: c4d.Vector = p - q
# Iterate over all points in the segment and adjust their position so that points[b] == p and
# points[a] == points[a], and all other points cubically interpolated in between.
for segmentIndex, pointIndex in enumerate(range(a, b + 1)):
weight: float = (float(segmentIndex) / float(count)) ** 3
print (segmentIndex, pointIndex, weight)
points[pointIndex] += (delta * weight)
spline.SetAllPoints(points)
spline.Message(c4d.MSG_UPDATE)
```