feat(Blenvy): fixed & overhauled components processing & hashing

* now correctly using nested long names + attribute name for structs
 * so no more hashing collisions for all test cases !
 * restructured internals of property group generation & registration
 * dynamically generated property group classes are now unregistered correctly
 * lots of minor related tweaks & improvements
This commit is contained in:
kaosat.dev 2024-06-17 11:44:08 +02:00
parent e86920168a
commit 2abdb7f64e
6 changed files with 54 additions and 36 deletions

View File

@ -95,7 +95,7 @@ Components:
-> VERY likely due to the int-offset computation for hashes of components -> VERY likely due to the int-offset computation for hashes of components
- now switched to tiger_hash - now switched to tiger_hash
- [x] add warning about hash colision (not much we can/ could do if it is the case ?) - [x] add warning about hash colision (not much we can/ could do if it is the case ?)
- [ ] double check weird collisions AND/OR reuse existing if applicable - [x] double check weird collisions AND/OR reuse existing if applicable
- [x] annoying default path for registry, should be relative to the assets path - [x] annoying default path for registry, should be relative to the assets path
@ -163,8 +163,9 @@ General issues:
- [ ] remove/replace bevy editor pls with some native ui to display hierarchies - [ ] remove/replace bevy editor pls with some native ui to display hierarchies
- [ ] switch to bevy rc2 - [ ] switch to bevy rc2
- [ ] simplify examples:
- [ ] a full fledged demo (including physics & co)
- [ ] other examples without interactions or physics
- [x] overall cleanup - [x] overall cleanup

View File

@ -220,7 +220,7 @@ def upsert_component_in_item(item, long_name, registry):
if property_group_name in registry.component_propertyGroups: if property_group_name in registry.component_propertyGroups:
# we have found a matching property_group, so try to inject it # we have found a matching property_group, so try to inject it
# now inject property group # now inject property group
setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as all ComponentMetadata get the propGroup, but have not found a way to assign it per instance setattr(ComponentMetadata, property_group_name, registry.component_propertyGroups[property_group_name]) # FIXME: not ideal as ALL instances of ComponentMetadata get the propGroup, but have not found a way to assign it per instance
propertyGroup = getattr(component_meta, property_group_name, None) propertyGroup = getattr(component_meta, property_group_name, None)
# now deal with property groups details # now deal with property groups details

View File

@ -30,13 +30,14 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
with_list = False with_list = False
with_map = False with_map = False
padding = " " * (len(nesting_long_names) + 1) #padding = " " * (len(nesting_long_names) + 1)
#print(f"{padding}process component", long_name, "nesting_long_names",nesting_long_names, "foo", has_properties, has_prefixItems, is_enum, is_list, is_map) #print(f"{padding}process component", long_name, "nesting_long_names",nesting_long_names, "foo", has_properties, has_prefixItems, is_enum, is_list, is_map)
if has_properties: if has_properties:
__annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names) __annotations__ = __annotations__ | process_structs.process_structs(registry, definition, properties, update, nesting_long_names)
with_properties = True with_properties = True
tupple_or_struct = "struct" tupple_or_struct = "struct"
#print(f"{padding}struct")
if has_prefixItems: if has_prefixItems:
__annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting_long_names) __annotations__ = __annotations__ | process_tupples.process_tupples(registry, definition, prefixItems, update, nesting_long_names)
@ -61,7 +62,6 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
for a in __annotations__: for a in __annotations__:
field_names.append(a) field_names.append(a)
extras = extras if extras is not None else { extras = extras if extras is not None else {
"long_name": long_name "long_name": long_name
} }
@ -78,25 +78,16 @@ def process_component(registry, definition, update, extras=None, nesting_long_na
**dict(with_properties = with_properties, with_items= with_items, with_enum= with_enum, with_list= with_list, with_map = with_map, short_name= short_name, long_name=long_name), **dict(with_properties = with_properties, with_items= with_items, with_enum= with_enum, with_list= with_list, with_map = with_map, short_name= short_name, long_name=long_name),
'root_component': root_component 'root_component': root_component
} }
#FIXME: YIKES, but have not found another way:
""" Withouth this ; the following does not work # we need to pass the full hierarchy to disambiguate between components
# Withouth this ; the following does not work
"""
-BasicTest -BasicTest
- NestingTestLevel2 - NestingTestLevel2
-BasicTest => the registration & update callback of this one overwrites the first "basicTest" -BasicTest => the registration & update callback of this one overwrites the first "basicTest"
have not found a cleaner workaround so far
""" """
property_group_name = registry.generate_propGroup_name(nesting_long_names)
(property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params)
# add our component propertyGroup to the registry # add our component propertyGroup to the registry
registry.register_component_propertyGroup(property_group_name, property_group_pointer) (property_group_pointer, property_group_class) = registry.register_component_propertyGroup(nesting_long_names, property_group_params)
return (property_group_pointer, property_group_class) return (property_group_pointer, property_group_class)
def property_group_from_infos(property_group_name, property_group_parameters):
# print("creating property group", property_group_name)
property_group_class = type(property_group_name, (PropertyGroup,), property_group_parameters)
bpy.utils.register_class(property_group_class)
property_group_pointer = PointerProperty(type=property_group_class)
return (property_group_pointer, property_group_class)

View File

@ -33,7 +33,7 @@ def process_structs(registry, definition, properties, update, nesting_long_names
__annotations__[property_name] = blender_property __annotations__[property_name] = blender_property
else: else:
original_long_name = original["long_name"] original_long_name = original["long_name"]
(sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "long_name": original_long_name}, nesting_long_names) (sub_component_group, _) = process_component.process_component(registry, original, update, {"nested": True, "long_name": original_long_name}, nesting_long_names+[property_name])
__annotations__[property_name] = sub_component_group __annotations__[property_name] = sub_component_group
# if there are sub fields, add an attribute "sub_fields" possibly a pointer property ? or add a standard field to the type , that is stored under "attributes" and not __annotations (better) # if there are sub fields, add an attribute "sub_fields" possibly a pointer property ? or add a standard field to the type , that is stored under "attributes" and not __annotations (better)
else: else:

View File

@ -34,12 +34,10 @@ def generate_propertyGroups_for_components():
type_infos = registry.type_infos type_infos = registry.type_infos
for component_name in type_infos: for root_type_name in type_infos:
definition = type_infos[component_name] definition = type_infos[root_type_name]
is_component = definition['isComponent'] if "isComponent" in definition else False #print("root property", component_name,f"({is_component})")
root_property_name = component_name# if is_component else None process_component(registry, definition, update_calback_helper(definition, update_component, root_type_name), extras=None, nesting_long_names=[])
print("root property", component_name,f"({is_component})")
process_component(registry, definition, update_calback_helper(definition, update_component, root_property_name), extras=None, nesting_long_names=[])
# if we had to add any wrapper types on the fly, process them now # if we had to add any wrapper types on the fly, process them now
registry.process_custom_types() registry.process_custom_types()

View File

@ -7,6 +7,8 @@ from bpy_types import (PropertyGroup)
from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty) from bpy.props import (StringProperty, BoolProperty, FloatProperty, FloatVectorProperty, IntProperty, IntVectorProperty, EnumProperty, PointerProperty, CollectionProperty)
from ..components.metadata import ComponentMetadata from ..components.metadata import ComponentMetadata
from .hashing.tiger import hash as tiger_hash from .hashing.tiger import hash as tiger_hash
# helper class to store missing bevy types information # helper class to store missing bevy types information
class MissingBevyType(bpy.types.PropertyGroup): class MissingBevyType(bpy.types.PropertyGroup):
long_name: bpy.props.StringProperty( long_name: bpy.props.StringProperty(
@ -14,6 +16,15 @@ class MissingBevyType(bpy.types.PropertyGroup):
) # type: ignore ) # type: ignore
def property_group_from_infos(property_group_name, property_group_parameters):
# print("creating property group", property_group_name)
property_group_class = type(property_group_name, (PropertyGroup,), property_group_parameters)
bpy.utils.register_class(property_group_class)
property_group_pointer = PointerProperty(type=property_group_class)
return (property_group_pointer, property_group_class)
# this is where we store the information for all available components # this is where we store the information for all available components
class ComponentsRegistry(PropertyGroup): class ComponentsRegistry(PropertyGroup):
registry: bpy.props. StringProperty( registry: bpy.props. StringProperty(
@ -140,6 +151,8 @@ class ComponentsRegistry(PropertyGroup):
type_infos = {} type_infos = {}
type_infos_missing = [] type_infos_missing = []
component_propertyGroups = {} component_propertyGroups = {}
component_property_group_classes = []
custom_types_to_add = {} custom_types_to_add = {}
invalid_components = [] invalid_components = []
@ -152,8 +165,18 @@ class ComponentsRegistry(PropertyGroup):
for propgroup_name in cls.component_propertyGroups.keys(): for propgroup_name in cls.component_propertyGroups.keys():
try: try:
delattr(ComponentMetadata, propgroup_name) delattr(ComponentMetadata, propgroup_name)
#print("sucess REMOVAL from Metadata")
except Exception as error: except Exception as error:
pass pass
#print("failed to unregister")
for propgroup_class in cls.component_property_group_classes:
try:
bpy.utils.unregister_class(propgroup_class)
#print("sucess UNREGISTER")
except Exception as error:
pass
#print("NEW failed to unregister")
del bpy.types.WindowManager.components_registry del bpy.types.WindowManager.components_registry
@ -167,7 +190,10 @@ class ComponentsRegistry(PropertyGroup):
self.missing_types_list.clear() self.missing_types_list.clear()
self.type_infos.clear() self.type_infos.clear()
self.type_infos_missing.clear() self.type_infos_missing.clear()
self.component_propertyGroups.clear() self.component_propertyGroups.clear()
self.component_property_group_classes.clear()
self.custom_types_to_add.clear() self.custom_types_to_add.clear()
self.invalid_components.clear() self.invalid_components.clear()
# now prepare paths to load data # now prepare paths to load data
@ -188,10 +214,6 @@ class ComponentsRegistry(PropertyGroup):
def has_type_infos(self): def has_type_infos(self):
return len(self.type_infos.keys()) != 0 return len(self.type_infos.keys()) != 0
# we keep a list of component propertyGroup around
def register_component_propertyGroup(self, name, propertyGroup):
self.component_propertyGroups[name] = propertyGroup
# to be able to give the user more feedback on any missin/unregistered types in their schema file # to be able to give the user more feedback on any missin/unregistered types in their schema file
def add_missing_typeInfo(self, long_name): def add_missing_typeInfo(self, long_name):
if not long_name in self.type_infos_missing: if not long_name in self.type_infos_missing:
@ -217,18 +239,25 @@ class ComponentsRegistry(PropertyGroup):
long_names_to_propgroup_names = {} long_names_to_propgroup_names = {}
# we keep a list of component propertyGroup around
def register_component_propertyGroup(self, nesting, property_group_params):
property_group_name = self.generate_propGroup_name(nesting)
(property_group_pointer, property_group_class) = property_group_from_infos(property_group_name, property_group_params)
self.component_propertyGroups[property_group_name] = property_group_pointer
self.component_property_group_classes.append(property_group_class)
return (property_group_pointer, property_group_class)
# generate propGroup name from nesting level: each longName + nesting is unique # generate propGroup name from nesting level: each longName + nesting is unique
def generate_propGroup_name(self, nesting): def generate_propGroup_name(self, nesting):
#print("gen propGroup name for", shortName, nesting)
key = str(nesting) key = str(nesting)
propGroupHash = tiger_hash(key) propGroupHash = tiger_hash(key)
propGroupName = propGroupHash + "_ui" propGroupName = propGroupHash + "_ui"
# check for collision # check for collision
padding = " " * (len(nesting) + 1) #padding = " " * (len(nesting) + 1)
#print(f"{padding}--computing hash for", nesting)
print(f"{padding}--computing hash for", nesting)
if propGroupName in self.long_names_to_propgroup_names.values(): if propGroupName in self.long_names_to_propgroup_names.values():
print(" WARNING !! you have a collision between the hash of multiple component names: collision for", nesting) print(" WARNING !! you have a collision between the hash of multiple component names: collision for", nesting)
@ -238,7 +267,6 @@ class ComponentsRegistry(PropertyGroup):
def get_propertyGroupName_from_longName(self, longName): def get_propertyGroupName_from_longName(self, longName):
return self.long_names_to_propgroup_names.get(str([longName]), None) return self.long_names_to_propgroup_names.get(str([longName]), None)
########### ###########