With the migration from GPv2 to GPv3, the Python API has to be reimplemented. In the past weeks, there have been some discussions on how this should be done. I’d like to summarize all the thoughts so far, point out concerns from addon developers and finally ask other Blender developers to give their opinion.
Python API in GPv2
In GPv2, strokes and points are their own structs. This is reflected in the Python API. Each stroke and each point is its own object.
Here’s an example:
for layer in gp.layers:
for frame in layer.frames:
if frame.frame_number >= 20:
continue
for stroke in frame.strokes:
if stroke.material_index != 0:
stroke.select = False
continue
stroke.select = True
for point in stroke.points:
if point.pressure > 0.6:
print(point.co, point.pressure)
point.vertex_color = (1, 0, 0)
I’d like to go over the options for possible implementations of this API for GPv3:
Option 1
The first option is to simply stay as close to the previous API as possible.
See #121407 - WIP: GPv3: RNA for frame, drawing, stroke and point - blender - Blender Projects for an implementation.
Pros:
- Virtually no change to the old API, easy to understand (object oriented).
- Easy for python developers to port their addons.
Cons:
- Doesn’t represent the underlying data, has to implement an abstraction layer in RNA on top.
- No access to custom attributes, not extendable (each attribute has to be exposed individually).
Option 2
Use the existing attributes Python API (rna_attribute.cc
).
Pros:
- Has an existing implementation.
- Is extendable (attributes are retrieved by their name).
Cons:
- Invalidation. Currently the API doesn’t take care of invalidation. E.g.
>>> cube = bpy.data.objects["Cube"].data >>> positions = cube.attributes["position"] >>> cube.attributes.new("custom", 'FLOAT', 'POINT') ... >>> positions.data bpy.data.meshes['Cube'].attributes["custom"].data
- The API is a bit fiddly to use, because of current limitations in RNA. E.g.
Notice the extra step of indirection:>>> cube = bpy.data.objects["Cube"].data >>> cube.attributes["position"].data[0].vector Vector((1.0, 1.0, 1.0))
attributes["position"].data
is aFloatVectorAttribute
butdata[0]
is aFloatVectorAttributeValue
with a propertyvector
that is the actualVector
. This makes writing code like the GPv2 example above much more verbose and confusing.
Option 3
Implement a custom python object implementation for attribute arrays.
Similar to the IDProperty
implementation. See #122094 - WIP: GPv3: Python API for frame, drawing and builtin geometry attributes - blender - Blender Projects for more details (thanks @Sietse-Brouwer !).
Pros:
- Easier to understand, numpy-like, API.
- Is extendable, and much more customizable.
- Orthogonal to current attribute API (access using
.data_array
).
Cons:
- New implementation (ideally this would replace the current attributes API eventually, so we don’t have to maintain the two)
- Invalidation. Similar to Option 2, this does not cover cache invalidation at the moment.
Personally, I believe that we should: Consider Option 3 and try to make it work and fall back to Option 1 if we can’t.
In talking to addon developers, I don’t think Option 2 would work and would have to be changed afterwards, breaking the API again.
Since we’re going to have to break the API, I think we should go for something that actually represents GPv3s data layout well.
Let me know your thoughts, thank you.