tree-clipper 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tree_clipper/__init__.py +0 -0
- tree_clipper/common.py +150 -0
- tree_clipper/dynamic_pointer.py +122 -0
- tree_clipper/export_nodes.py +709 -0
- tree_clipper/id_data_getter.py +127 -0
- tree_clipper/import_nodes.py +654 -0
- tree_clipper/scene_info.py +162 -0
- tree_clipper/specific_abstract.py +280 -0
- tree_clipper/specific_handlers.py +1465 -0
- tree_clipper-0.1.0.dist-info/METADATA +26 -0
- tree_clipper-0.1.0.dist-info/RECORD +13 -0
- tree_clipper-0.1.0.dist-info/WHEEL +4 -0
- tree_clipper-0.1.0.dist-info/licenses/LICENSE +674 -0
tree_clipper/__init__.py
ADDED
|
File without changes
|
tree_clipper/common.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import bpy
|
|
2
|
+
|
|
3
|
+
from types import NoneType
|
|
4
|
+
from typing import Any, Callable, TYPE_CHECKING
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import tempfile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .export_nodes import Exporter
|
|
11
|
+
from .import_nodes import Importer
|
|
12
|
+
|
|
13
|
+
# these fields are in the top level JSON object
|
|
14
|
+
BLENDER_VERSION = "blender_version"
|
|
15
|
+
TREE_CLIPPER_VERSION = "tree_clipper_version"
|
|
16
|
+
CURRENT_TREE_CLIPPER_VERSION = "0.1.0" # tested to match pyproject.toml
|
|
17
|
+
MATERIAL_NAME = "name"
|
|
18
|
+
TREES = "node_trees"
|
|
19
|
+
EXTERNAL = "external"
|
|
20
|
+
SCENES = "scenes"
|
|
21
|
+
|
|
22
|
+
# within each external item
|
|
23
|
+
EXTERNAL_DESCRIPTION = "description"
|
|
24
|
+
EXTERNAL_FIXED_TYPE_NAME = "fixed_type_name"
|
|
25
|
+
EXTERNAL_SCENE_ID = "scene_id"
|
|
26
|
+
|
|
27
|
+
# for every object
|
|
28
|
+
ID = "id" # to reference it from elsewhere
|
|
29
|
+
DATA = "data" # the actual data
|
|
30
|
+
FROM_ROOT = "from_root" # optional, for debugging
|
|
31
|
+
|
|
32
|
+
# every compressed serialization starts with this
|
|
33
|
+
MAGIC_STRING = "TreeClipper::"
|
|
34
|
+
|
|
35
|
+
# to help prevent typos
|
|
36
|
+
BL_RNA = "bl_rna"
|
|
37
|
+
BL_IDNAME = "bl_idname"
|
|
38
|
+
RNA_TYPE = "rna_type" # TODO: another 'forbidden' category?
|
|
39
|
+
DEFAULT_VALUE = "default_value"
|
|
40
|
+
DISPLAY_SHAPE = "display_shape"
|
|
41
|
+
ITEMS = "items"
|
|
42
|
+
NAME = "name"
|
|
43
|
+
PROP_TYPE_BOOLEAN = "BOOLEAN"
|
|
44
|
+
PROP_TYPE_INT = "INT"
|
|
45
|
+
PROP_TYPE_FLOAT = "FLOAT"
|
|
46
|
+
PROP_TYPE_STRING = "STRING"
|
|
47
|
+
PROP_TYPE_ENUM = "ENUM"
|
|
48
|
+
PROP_TYPE_POINTER = "POINTER"
|
|
49
|
+
PROP_TYPE_COLLECTION = "COLLECTION"
|
|
50
|
+
SIMPLE_PROPERTY_TYPES_AS_STRS = set(
|
|
51
|
+
[
|
|
52
|
+
PROP_TYPE_BOOLEAN,
|
|
53
|
+
PROP_TYPE_INT,
|
|
54
|
+
PROP_TYPE_FLOAT,
|
|
55
|
+
PROP_TYPE_STRING,
|
|
56
|
+
PROP_TYPE_ENUM,
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
NODE_TREE = "node_tree"
|
|
60
|
+
DIMENSIONS = "dimensions"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# bl_* properties can be dangerous to set
|
|
64
|
+
# https://github.com/Algebraic-UG/tree_clipper/issues/39
|
|
65
|
+
# they should probably be read-only in most cases?
|
|
66
|
+
FORBIDDEN_PROPERTIES = [
|
|
67
|
+
"bl_idname",
|
|
68
|
+
"bl_label",
|
|
69
|
+
"bl_subtype_label",
|
|
70
|
+
"bl_static_type",
|
|
71
|
+
"bl_description",
|
|
72
|
+
"bl_icon",
|
|
73
|
+
"bl_width_default",
|
|
74
|
+
"bl_width_min",
|
|
75
|
+
"bl_width_max",
|
|
76
|
+
"bl_height_default",
|
|
77
|
+
"bl_height_min",
|
|
78
|
+
"bl_height_max",
|
|
79
|
+
"bl_socket_idname",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def no_clobber(data: dict, key: str | int, value) -> None:
|
|
84
|
+
if key in data:
|
|
85
|
+
raise RuntimeError(f"Clobbering '{key}'")
|
|
86
|
+
data[key] = value
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FromRoot:
|
|
90
|
+
def __init__(self, path: list) -> None:
|
|
91
|
+
self.path = path
|
|
92
|
+
|
|
93
|
+
def add(self, piece: str) -> "FromRoot":
|
|
94
|
+
return FromRoot(self.path + [piece])
|
|
95
|
+
|
|
96
|
+
def add_prop(self, prop: bpy.types.Property) -> "FromRoot":
|
|
97
|
+
return self.add(f"{prop.type} ({prop.identifier})")
|
|
98
|
+
|
|
99
|
+
def to_str(self) -> str:
|
|
100
|
+
return str(" -> ".join(self.path))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def most_specific_type_handled(
|
|
104
|
+
specific_handlers: dict[type, Callable],
|
|
105
|
+
obj: bpy.types.bpy_struct,
|
|
106
|
+
) -> type:
|
|
107
|
+
# collections are too weird, this is False:
|
|
108
|
+
# type(bpy.data.node_groups['Geometry Nodes'].nodes) == bpy.types.Nodes
|
|
109
|
+
if isinstance(obj, bpy.types.bpy_prop_collection):
|
|
110
|
+
return next(
|
|
111
|
+
(
|
|
112
|
+
ty
|
|
113
|
+
for ty in specific_handlers.keys()
|
|
114
|
+
if ty != NoneType and ty.bl_rna.identifier == obj.bl_rna.identifier # type: ignore
|
|
115
|
+
),
|
|
116
|
+
NoneType,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
ty = type(obj)
|
|
120
|
+
while True:
|
|
121
|
+
if ty in specific_handlers.keys():
|
|
122
|
+
return ty
|
|
123
|
+
if len(ty.__bases__) == 0:
|
|
124
|
+
return NoneType
|
|
125
|
+
if len(ty.__bases__) > 1:
|
|
126
|
+
raise RuntimeError(f"multiple inheritence {ty}, unclear what to choose")
|
|
127
|
+
ty = ty.__bases__[0]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
GETTER = Callable[[], bpy.types.bpy_struct]
|
|
131
|
+
SERIALIZER = Callable[["Exporter", bpy.types.bpy_struct, FromRoot], dict[str, Any]]
|
|
132
|
+
DESERIALIZER = Callable[["Importer", GETTER, dict, FromRoot], None]
|
|
133
|
+
SIMPLE_DATA_TYPE = list[str] | list[float] | list[int] | str | float | int
|
|
134
|
+
SIMPLE_PROP_TYPE = (
|
|
135
|
+
bpy.types.BoolProperty
|
|
136
|
+
| bpy.types.IntProperty
|
|
137
|
+
| bpy.types.FloatProperty
|
|
138
|
+
| bpy.types.StringProperty
|
|
139
|
+
| bpy.types.EnumProperty
|
|
140
|
+
)
|
|
141
|
+
SIMPLE_PROP_TYPE_TUPLE = (
|
|
142
|
+
bpy.types.BoolProperty,
|
|
143
|
+
bpy.types.IntProperty,
|
|
144
|
+
bpy.types.FloatProperty,
|
|
145
|
+
bpy.types.StringProperty,
|
|
146
|
+
bpy.types.EnumProperty,
|
|
147
|
+
)
|
|
148
|
+
EXTERNAL_SERIALIZATION = dict[str, int | str | None]
|
|
149
|
+
|
|
150
|
+
DEFAULT_FILE = str(Path(tempfile.gettempdir()) / "default.json")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import bpy
|
|
2
|
+
|
|
3
|
+
from typing import Type
|
|
4
|
+
|
|
5
|
+
from .common import no_clobber
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# TODO: we might need to return a list that fits the Blender version
|
|
9
|
+
KNOWN_POINTABLES = {
|
|
10
|
+
bpy.types.SunLight,
|
|
11
|
+
bpy.types.Texture,
|
|
12
|
+
bpy.types.Object,
|
|
13
|
+
bpy.types.WorkSpace,
|
|
14
|
+
bpy.types.Mesh,
|
|
15
|
+
bpy.types.Text,
|
|
16
|
+
bpy.types.Lattice,
|
|
17
|
+
bpy.types.Material,
|
|
18
|
+
bpy.types.Camera,
|
|
19
|
+
bpy.types.World,
|
|
20
|
+
bpy.types.Volume,
|
|
21
|
+
bpy.types.FreestyleLineStyle,
|
|
22
|
+
bpy.types.MovieClip,
|
|
23
|
+
bpy.types.PointLight,
|
|
24
|
+
bpy.types.LightProbeVolume,
|
|
25
|
+
bpy.types.TextureNodeTree,
|
|
26
|
+
bpy.types.AreaLight,
|
|
27
|
+
bpy.types.VoronoiTexture,
|
|
28
|
+
bpy.types.CompositorNodeTree,
|
|
29
|
+
bpy.types.NoiseTexture,
|
|
30
|
+
bpy.types.Image,
|
|
31
|
+
bpy.types.SpotLight,
|
|
32
|
+
bpy.types.ImageTexture,
|
|
33
|
+
bpy.types.VectorFont,
|
|
34
|
+
bpy.types.ParticleSettings,
|
|
35
|
+
bpy.types.Screen,
|
|
36
|
+
bpy.types.Annotation,
|
|
37
|
+
bpy.types.MagicTexture,
|
|
38
|
+
bpy.types.MetaBall,
|
|
39
|
+
bpy.types.Key,
|
|
40
|
+
bpy.types.MarbleTexture,
|
|
41
|
+
bpy.types.MusgraveTexture,
|
|
42
|
+
bpy.types.StucciTexture,
|
|
43
|
+
bpy.types.WoodTexture,
|
|
44
|
+
bpy.types.DistortedNoiseTexture,
|
|
45
|
+
bpy.types.LightProbeSphere,
|
|
46
|
+
bpy.types.Scene,
|
|
47
|
+
bpy.types.CloudsTexture,
|
|
48
|
+
bpy.types.Brush,
|
|
49
|
+
bpy.types.WindowManager,
|
|
50
|
+
bpy.types.Library,
|
|
51
|
+
bpy.types.Collection,
|
|
52
|
+
bpy.types.Sound,
|
|
53
|
+
bpy.types.NodeTree,
|
|
54
|
+
bpy.types.GreasePencil,
|
|
55
|
+
bpy.types.Curves,
|
|
56
|
+
bpy.types.Armature,
|
|
57
|
+
bpy.types.Light,
|
|
58
|
+
bpy.types.Curve,
|
|
59
|
+
bpy.types.Speaker,
|
|
60
|
+
bpy.types.Action,
|
|
61
|
+
bpy.types.GeometryNodeTree,
|
|
62
|
+
bpy.types.ShaderNodeTree,
|
|
63
|
+
bpy.types.PointCloud,
|
|
64
|
+
bpy.types.LightProbe,
|
|
65
|
+
bpy.types.CacheFile,
|
|
66
|
+
bpy.types.TextCurve,
|
|
67
|
+
bpy.types.BlendTexture,
|
|
68
|
+
bpy.types.Mask,
|
|
69
|
+
bpy.types.PaintCurve,
|
|
70
|
+
bpy.types.LightProbePlane,
|
|
71
|
+
bpy.types.Palette,
|
|
72
|
+
bpy.types.SurfaceCurve,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def add_all_known_pointer_properties(
|
|
77
|
+
*,
|
|
78
|
+
cls: Type[bpy.types.PropertyGroup],
|
|
79
|
+
prefix: str,
|
|
80
|
+
):
|
|
81
|
+
def get_pointer_property_name(ty: type):
|
|
82
|
+
return f"{prefix}{ty.__name__}"
|
|
83
|
+
|
|
84
|
+
# does this even ever happen
|
|
85
|
+
if not hasattr(cls, "__annotations__"):
|
|
86
|
+
cls.__annotations__ = {}
|
|
87
|
+
|
|
88
|
+
# we store which kind of thing we're pointing to, used in get_pointer
|
|
89
|
+
no_clobber(
|
|
90
|
+
cls.__annotations__,
|
|
91
|
+
"active_ptr_type_name",
|
|
92
|
+
bpy.props.StringProperty(),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# now actually register all the properties
|
|
96
|
+
for pointable in KNOWN_POINTABLES:
|
|
97
|
+
no_clobber(
|
|
98
|
+
cls.__annotations__,
|
|
99
|
+
get_pointer_property_name(pointable),
|
|
100
|
+
bpy.props.PointerProperty(type=pointable),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# this switches the type we're pointing to and clears all
|
|
104
|
+
def set_active_pointer_type(self, type_name: str):
|
|
105
|
+
self.active_ptr_type_name = type_name
|
|
106
|
+
for ty in KNOWN_POINTABLES:
|
|
107
|
+
setattr(self, get_pointer_property_name(ty), None)
|
|
108
|
+
|
|
109
|
+
# this is needed to display the property
|
|
110
|
+
def get_active_pointer_identifier(self) -> str:
|
|
111
|
+
return f"{prefix}{self.active_ptr_type_name}"
|
|
112
|
+
|
|
113
|
+
# directly return the pointer
|
|
114
|
+
def get_active_pointer(self) -> bpy.types.PointerProperty:
|
|
115
|
+
return getattr(self, self.get_active_pointer_identifier())
|
|
116
|
+
|
|
117
|
+
assert not hasattr(cls, set_active_pointer_type.__name__)
|
|
118
|
+
setattr(cls, set_active_pointer_type.__name__, set_active_pointer_type)
|
|
119
|
+
assert not hasattr(cls, get_active_pointer_identifier.__name__)
|
|
120
|
+
setattr(cls, get_active_pointer_identifier.__name__, get_active_pointer_identifier)
|
|
121
|
+
assert not hasattr(cls, get_active_pointer.__name__)
|
|
122
|
+
setattr(cls, get_active_pointer.__name__, get_active_pointer)
|