diff --git a/bbp_ng/PROP_ballance_element.py b/bbp_ng/PROP_ballance_element.py index 26eda1b..f76c754 100644 --- a/bbp_ng/PROP_ballance_element.py +++ b/bbp_ng/PROP_ballance_element.py @@ -1,60 +1,70 @@ import bpy -import os, typing +import os, typing, enum from . import UTIL_functions, UTIL_blender_mesh #region Raw Elements Operations -_g_AllElementsName: tuple[str] = ( - "P_Extra_Life", - "P_Extra_Point", - "P_Trafo_Paper", - "P_Trafo_Stone", - "P_Trafo_Wood", - "P_Ball_Paper", - "P_Ball_Stone", - "P_Ball_Wood", - "P_Box", - "P_Dome", - "P_Modul_01", - "P_Modul_03", - "P_Modul_08", - "P_Modul_17", - "P_Modul_18", - "P_Modul_19", - "P_Modul_25", - "P_Modul_26", - "P_Modul_29", - "P_Modul_30", - "P_Modul_34", - "P_Modul_37", - "P_Modul_41", - "PC_TwoFlames", - "PE_Balloon", - "PR_Resetpoint", - "PS_FourFlames", -) +class BallanceElementType(enum.IntEnum): + P_Extra_Life = 0 + P_Extra_Point = 1 + P_Trafo_Paper = 2 + P_Trafo_Stone = 3 + P_Trafo_Wood = 4 + P_Ball_Paper = 5 + P_Ball_Stone = 6 + P_Ball_Wood = 7 + P_Box = 8 + P_Dome = 9 + P_Modul_01 = 10 + P_Modul_03 = 11 + P_Modul_08 = 12 + P_Modul_17 = 13 + P_Modul_18 = 14 + P_Modul_19 = 15 + P_Modul_25 = 16 + P_Modul_26 = 17 + P_Modul_29 = 18 + P_Modul_30 = 19 + P_Modul_34 = 20 + P_Modul_37 = 21 + P_Modul_41 = 22 + PC_TwoFlames = 23 + PE_Balloon = 24 + PR_Resetpoint = 25 + PS_FourFlames = 26 -_g_ElementIndexMap: dict[str, int] = dict((val, idx) for idx, val in enumerate(_g_AllElementsName)) +_g_ElementCount: int = len(BallanceElementType) +_g_ElementNameIdMap: dict[str, int] = dict((entry.name, entry.value) for entry in BallanceElementType) +_g_ElementIdNameMap: dict[str, int] = dict((entry.value, entry.name) for entry in BallanceElementType) -def get_ballance_element_index(name: str) -> int | None: +def get_ballance_element_id(name: str) -> int | None: """ - Get Ballance element index by its name. + Get Ballance element ID by its name. @param name[in] The name of element - @return the index of this Ballance element name distributed by this plugin. or None if providing name is invalid. + @return the ID of this Ballance element name distributed by this plugin. or None if providing name is invalid. """ - return _g_ElementIndexMap.get(name, None) + return _g_ElementNameIdMap.get(name, None) + +def get_ballance_element_name(id: int) -> int | None: + """ + Get Ballance element name by its ID + + @param id[in] The ID of element + @return the name of this Ballance element, or None if ID is invalid. + """ + return _g_ElementIdNameMap.get(id, None) def is_ballance_element(name: str) -> bool: """ Check whether providing name is Ballance element. - Just a wrapper of get_ballance_element_index + Just a wrapper of get_ballance_element_id @param name[in] The name of element @return True if providing name is Ballance element name. """ - return get_ballance_element_index(name) is not None + return get_ballance_element_id(name) is not None #endregion @@ -76,15 +86,42 @@ def get_ballance_elements() -> bpy.types.CollectionProperty: #endregion -#region Ballance Elements Operation Help Class +#region + +def load_element(mesh: bpy.types.Mesh, element_id: int) -> None: + pass + +#endregion + +#region Ballance Elements Operation Help Class & Functions class BallanceElementsHelper(): - __mSingletonOccupation: typing.ClassVar[bool] = False + """ + The helper of Ballance elements processing. + + All element operations, including getting or setting, must be manipulated by this class. + You should NOT operate Ballance Elements property (in Scene) directly. + + This class should only have 1 instance at the same time. This class support `with` syntax to achieve this. + This class frequently used in importing stage to create element placeholder. + """ + + __mSingletonMutex: typing.ClassVar[bool] = False __mIsValid: bool __mElementMap: dict[int, bpy.types.Mesh] - def __init__(): - pass + def __init__(self): + self.__mElementMap = {} + + # check singleton + if BallanceElementsHelper.__mSingletonMutex: + self.__mIsValid = False + raise UTIL_functions.BBPException('BallanceElementsHelper is mutex.') + + # set validation and read ballance elements property + BallanceElementsHelper.__mSingletonMutex = True + self.__mIsValid = True + self.__read_from_ballance_element() def is_valid(self) -> bool: return self.__mIsValid @@ -97,16 +134,80 @@ class BallanceElementsHelper(): def dispose(self) -> None: if self.is_valid(): - pass + # write to ballance elements property and reset validation + self.__write_to_ballance_elements() + self.__mIsValid = False + self.__mSingletonMutex = False - def load_element(self, element_idx: int) -> bpy.types.Mesh: - pass + def get_element(self, element_id: int) -> bpy.types.Mesh: + if not self.is_valid(): + raise UTIL_functions.BBPException('calling invalid BallanceElementsHelper') + + # get exist one + mesh: bpy.types.Mesh | None = self.__mElementMap.get(element_id, None) + if mesh is not None: + return mesh + + # if no existing one, create new one + new_mesh_name: str | None = get_ballance_element_id(element_id) + if new_mesh_name is None: + raise UTIL_functions.BBPException('invalid element id') + new_mesh: bpy.types.Mesh = bpy.data.meshes.new(get_ballance_element_name(element_id)) + + load_element(new_mesh, element_id) + self.__mElementMap[element_id] = new_mesh + return new_mesh def __write_to_ballance_elements(self) -> None: - pass + elements: bpy.types.CollectionProperty = get_ballance_elements() + elements.clear() + + for eleid, elemesh in self.__mElementMap.items(): + name: str | None = get_ballance_element_name(eleid) + if name is None: + continue + + item: BBP_PG_ballance_element = elements.add() + item.element_name = name + item.mesh_ptr = elemesh def __read_from_ballance_element(self) -> None: - pass + elements: bpy.types.CollectionProperty = get_ballance_elements() + self.__mElementMap.clear() + + item: BBP_PG_ballance_element + for item in elements: + # check requirements + if item.mesh_ptr is None: continue + mesh_id: int | None = get_ballance_element_id(item.element_name) + if mesh_id is None: continue + + # add into map + self.__mElementMap[mesh_id] = item.mesh_ptr + +def reset_ballance_elements() -> None: + invalid_idx: list[int] = [] + elements: bpy.types.CollectionProperty = get_ballance_elements() + + # re-load all elements + index: int = 0 + item: BBP_PG_ballance_element + for item in elements: + eleid: int | None = get_ballance_element_id(item.element_name) + + # load or record invalid entry + if eleid is None or item.mesh_ptr is None: + invalid_idx.append(index) + else: + load_element(item.mesh_ptr, eleid) + + # inc counter + index += 1 + + # remove invalid one with reversed order + invalid_idx.reverse() + for idx in invalid_idx: + elements.remove(idx) #endregion @@ -129,8 +230,7 @@ class BBP_OT_reset_ballance_elements(bpy.types.Operator): return context.scene is not None def execute(self, context): - scene: bpy.types.Scene = context.scene - + reset_ballance_elements() return {'FINISHED'} class BBP_PT_ballance_elements(bpy.types.Panel): @@ -163,8 +263,8 @@ class BBP_PT_ballance_elements(bpy.types.Panel): target, "active_ballance_elements", # default row height is a half of the count of all elements # limit the max row height to the the count of all elements - rows = len(_g_AllElementsName) // 2, - maxrows = len(_g_AllElementsName), + rows = _g_ElementCount // 2, + maxrows = _g_ElementCount, ) #endregion