Skip to content

Commit 2d56cd3

Browse files
General cleanup and refactor (#48)
* AttributeDomains -> str enum instead of Class * ruff check --fix * remove unnecessary methods * fix ruff
1 parent 1e8811b commit 2d56cd3

File tree

2 files changed

+69
-124
lines changed

2 files changed

+69
-124
lines changed

databpy/attribute.py

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from bpy.types import Object
66
import numpy as np
77

8-
COMPATIBLE_TYPES = [bpy.types.Mesh, bpy.types.Curve, bpy.types.PointCloud]
8+
COMPATIBLE_TYPES = [bpy.types.Mesh, bpy.types.Curves, bpy.types.PointCloud]
99

1010

1111
class NamedAttributeError(AttributeError):
@@ -76,29 +76,29 @@ class AttributeDomains(Enum):
7676
7777
Attributes
7878
----------
79-
POINT : AttributeDomain
79+
POINT : str
8080
The point domain of geometry data which includes vertices, point cloud and control points of curves.
81-
EDGE : AttributeDomain
81+
EDGE : str
8282
The edges of meshes, defined as pairs of vertices.
83-
FACE : AttributeDomain
83+
FACE : str
8484
The face domain of meshes, defined as groups of edges.
85-
CORNER : AttributeDomain
85+
CORNER : str
8686
The face domain of meshes, defined as pairs of edges that share a vertex.
87-
CURVE : AttributeDomain
87+
CURVE : str
8888
The Spline domain, which includes the individual splines that each contain at least one control point.
89-
INSTANCE : AttributeDomain
89+
INSTANCE : str
9090
The Instance domain, which can include sets of other geometry to be treated as a single group.
91-
LAYER : AttributeDomain
91+
LAYER : str
9292
The domain of single Grease Pencil layers.
9393
"""
9494

95-
POINT = AttributeDomain(name="POINT")
96-
EDGE = AttributeDomain(name="EDGE")
97-
FACE = AttributeDomain(name="FACE")
98-
CORNER = AttributeDomain(name="CORNER")
99-
CURVE = AttributeDomain(name="CURVE")
100-
INSTANCE = AttributeDomain(name="INSTNANCE")
101-
LAYER = AttributeDomain(name="LAYER")
95+
POINT = "POINT"
96+
EDGE = "EDGE"
97+
FACE = "FACE"
98+
CORNER = "CORNER"
99+
CURVE = "CURVE"
100+
INSTANCE = "INSTANCE"
101+
LAYER = "LAYER"
102102

103103

104104
@dataclass
@@ -331,21 +331,6 @@ def dtype(self) -> Type:
331331
def n_values(self) -> int:
332332
return np.prod(self.shape, dtype=int)
333333

334-
@classmethod
335-
def from_object(
336-
cls,
337-
obj: bpy.types.Object,
338-
name: str,
339-
atype: AttributeType,
340-
domain: AttributeDomain,
341-
):
342-
att = obj.data.get(name)
343-
if att is None:
344-
att = obj.data.attributes.new(
345-
name=name, type=atype.value.type_name, domain=domain.value.name
346-
)
347-
return Attribute(att)
348-
349334
def from_array(self, array: np.ndarray) -> None:
350335
"""
351336
Set the attribute data from a numpy array.
@@ -394,12 +379,45 @@ def __str__(self):
394379
)
395380

396381

382+
def _match_atype(
383+
atype: str | AttributeTypes | None, data: np.ndarray
384+
) -> AttributeTypes:
385+
if isinstance(atype, str):
386+
try:
387+
atype = AttributeTypes[atype]
388+
except KeyError:
389+
raise ValueError(
390+
f"Given data type {atype=} does not match any of the possible attribute types: {list(AttributeTypes)=}"
391+
)
392+
if atype is None:
393+
atype = guess_atype_from_array(data)
394+
return atype
395+
396+
397+
def _match_domain(
398+
domain: str | AttributeDomains | None,
399+
) -> str:
400+
if isinstance(domain, str):
401+
try:
402+
AttributeDomains[domain] # Validate the string is a valid domain
403+
return domain
404+
except KeyError:
405+
raise ValueError(
406+
f"Given domain {domain=} does not match any of the possible attribute domains: {list(AttributeDomains)=}"
407+
)
408+
if domain is None:
409+
return AttributeDomains.POINT.value
410+
if isinstance(domain, AttributeDomains):
411+
return domain.value
412+
return domain
413+
414+
397415
def store_named_attribute(
398416
obj: bpy.types.Object,
399417
data: np.ndarray,
400418
name: str,
401419
atype: str | AttributeTypes | None = None,
402-
domain: str | AttributeDomain | AttributeDomains = AttributeDomains.POINT,
420+
domain: str | AttributeDomains = AttributeDomains.POINT,
403421
overwrite: bool = True,
404422
) -> bpy.types.Attribute:
405423
"""
@@ -415,7 +433,7 @@ def store_named_attribute(
415433
The name of the attribute.
416434
atype : str or AttributeTypes or None, optional
417435
The attribute type to store the data as. If None, type is inferred from data.
418-
domain : str or AttributeDomain, optional
436+
domain : str or AttributeDomains, optional
419437
The domain of the attribute, by default 'POINT'.
420438
overwrite : bool, optional
421439
Whether to overwrite existing attribute, by default True.
@@ -446,41 +464,37 @@ def store_named_attribute(
446464
```
447465
"""
448466

449-
if isinstance(atype, str):
450-
try:
451-
atype = AttributeTypes[atype]
452-
except KeyError:
453-
raise ValueError(
454-
f"Given data type {atype=} does not match any of the possible attribute types: {list(AttributeTypes)=}"
455-
)
467+
atype = _match_atype(atype, data)
468+
domain = _match_domain(domain)
456469

457-
if isinstance(domain, str):
458-
try:
459-
domain = AttributeDomains[domain].value
460-
except KeyError:
461-
raise ValueError(
462-
f"Given domain {domain=} does not match any of the possible attribute domains: {list(AttributeDomains)=}"
463-
)
470+
if isinstance(obj, bpy.types.Object):
471+
obj_data = obj.data
472+
else:
473+
obj_data = obj.data
464474

465-
if atype is None:
466-
atype = guess_atype_from_array(data)
475+
if not isinstance(
476+
obj_data, (bpy.types.Mesh, bpy.types.Curves, bpy.types.PointCloud)
477+
):
478+
raise NamedAttributeError(
479+
f"Object must be a mesh, curve or point cloud to store attributes, not {type(obj_data)}"
480+
)
467481

468482
if name == "":
469483
raise NamedAttributeError("Attribute name cannot be an empty string.")
470484

471-
attribute = obj.data.attributes.get(name) # type: ignore
485+
attribute = obj_data.attributes.get(name) # type: ignore
472486
if not attribute or not overwrite:
473-
current_names = obj.data.attributes.keys()
474-
attribute = obj.data.attributes.new(name, atype.value.type_name, domain.name)
487+
current_names = obj_data.attributes.keys()
488+
attribute = obj_data.attributes.new(name, atype.value.type_name, domain)
475489

476490
if attribute is None:
477491
[
478-
obj.data.attributes.remove(obj.data.attributes[name])
479-
for name in obj.data.attributes.keys()
492+
obj_data.attributes.remove(obj_data.attributes[name])
493+
for name in obj_data.attributes.keys()
480494
if name not in current_names
481495
] # type: ignore
482496
raise NamedAttributeError(
483-
f"Could not create attribute `{name}` of type `{atype.value.type_name}` on domain `{domain.name}`. "
497+
f"Could not create attribute `{name}` of type `{atype.value.type_name}` on domain `{domain}`. "
484498
"Potentially the attribute name is too long or there is no geometry on the object for the given domain."
485499
)
486500

databpy/object.py

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
import bpy
44
import numpy as np
55
from bpy.types import Object
6-
from mathutils import Matrix
76
from numpy import typing as npt
87
from .array import AttributeArray
98

109
from . import attribute as attr
1110
from .addon import register
1211
from .attribute import (
13-
AttributeDomain,
1412
AttributeDomains,
1513
AttributeTypes,
1614
list_attributes,
@@ -313,7 +311,7 @@ def store_named_attribute(
313311
data: np.ndarray,
314312
name: str,
315313
atype: str | AttributeTypes | None = None,
316-
domain: str | AttributeDomain | AttributeDomains = AttributeDomains.POINT,
314+
domain: str | AttributeDomains = AttributeDomains.POINT,
317315
) -> None:
318316
"""
319317
Store a named attribute on the Blender object.
@@ -339,7 +337,6 @@ def store_named_attribute(
339337
attr.store_named_attribute(
340338
self.object, data=data, name=name, atype=atype, domain=domain
341339
)
342-
return self
343340

344341
def remove_named_attribute(self, name: str) -> None:
345342
"""
@@ -373,19 +370,6 @@ def named_attribute(self, name: str, evaluate: bool = False) -> np.ndarray:
373370
self._check_obj()
374371
return attr.named_attribute(self.object, name=name, evaluate=evaluate)
375372

376-
def set_boolean(self, array: np.ndarray, name: str) -> None:
377-
"""
378-
Store a boolean attribute on the Blender object.
379-
380-
Parameters
381-
----------
382-
array : np.ndarray
383-
The boolean data to be stored as an attribute.
384-
name : str
385-
The name for the attribute.
386-
"""
387-
self.store_named_attribute(array, name=name, atype=AttributeTypes.BOOLEAN)
388-
389373
def evaluate(self) -> Object:
390374
"""
391375
Return a version of the object with all modifiers applied.
@@ -463,40 +447,6 @@ def edges(self):
463447
"""
464448
return self.object.data.edges
465449

466-
def transform_origin(self, matrix: Matrix) -> None:
467-
"""
468-
Transform the origin of the Blender object.
469-
470-
Parameters
471-
----------
472-
matrix : Matrix
473-
The transformation matrix to apply to the origin.
474-
"""
475-
self.object.matrix_local = matrix * self.object.matrix_world
476-
477-
def transform_points(self, matrix: Matrix) -> None:
478-
"""
479-
Transform the points of the Blender object.
480-
481-
Parameters
482-
----------
483-
matrix : Matrix
484-
The transformation matrix to apply to the points.
485-
"""
486-
self.position = self.position * matrix
487-
488-
@property
489-
def selected(self) -> np.ndarray:
490-
"""
491-
Get the selected vertices of the Blender object.
492-
493-
Returns
494-
-------
495-
np.ndarray
496-
The selected vertices of the Blender object.
497-
"""
498-
return self.named_attribute(".select_vert")
499-
500450
@property
501451
def position(self) -> AttributeArray:
502452
"""
@@ -537,25 +487,6 @@ def position(self, value: np.ndarray) -> None:
537487
domain=AttributeDomains.POINT,
538488
)
539489

540-
def selected_positions(self, mask: np.ndarray | None = None) -> np.ndarray:
541-
"""
542-
Get the positions of the selected vertices, optionally filtered by a mask.
543-
544-
Parameters
545-
----------
546-
mask : np.ndarray | None, optional
547-
The mask to filter the selected vertices. Defaults to None.
548-
549-
Returns
550-
-------
551-
np.ndarray
552-
The positions of the selected vertices.
553-
"""
554-
if mask is not None:
555-
return self.position[np.logical_and(self.selected, mask)]
556-
557-
return self.position[self.selected]
558-
559490
def list_attributes(
560491
self, evaluate: bool = False, drop_hidden: bool = False
561492
) -> list[str]:

0 commit comments

Comments
 (0)