update component creation
This commit is contained in:
		@ -1,12 +1,47 @@
 | 
			
		||||
import bpy
 | 
			
		||||
import bpy, mathutils
 | 
			
		||||
import math, typing
 | 
			
		||||
from . import UTIL_functions, UTIL_icons_manager, UTIL_naming_convension
 | 
			
		||||
from . import PROP_ballance_element, PROP_virtools_group
 | 
			
		||||
 | 
			
		||||
#region Param Help Classes
 | 
			
		||||
 | 
			
		||||
class ComponentSectorParam():
 | 
			
		||||
    component_sector: bpy.props.IntProperty(
 | 
			
		||||
        name = "Sector",
 | 
			
		||||
        description = "Define which sector the object will be grouped in",
 | 
			
		||||
        min = 1, max = 999,
 | 
			
		||||
        soft_min = 1, soft_max = 8,
 | 
			
		||||
        default = 1,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def general_get_component_sector(self) -> int:
 | 
			
		||||
        return self.component_sector
 | 
			
		||||
    
 | 
			
		||||
    def draw_component_sector_params(self, layout: bpy.types.UILayout) -> None:
 | 
			
		||||
        layout.prop(self, 'component_sector')
 | 
			
		||||
 | 
			
		||||
class ComponentCountParam():
 | 
			
		||||
    component_count: bpy.props.IntProperty(
 | 
			
		||||
        name = "Count",
 | 
			
		||||
        description = "The count of components you want to generate",
 | 
			
		||||
        min = 1, max = 64,
 | 
			
		||||
        soft_min = 1, soft_max = 32,
 | 
			
		||||
        default = 1,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def general_get_component_count(self) -> int:
 | 
			
		||||
        return self.component_count
 | 
			
		||||
 | 
			
		||||
    def draw_component_count_params(self, layout: bpy.types.UILayout) -> None:
 | 
			
		||||
        layout.prop(self, 'component_count')
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
#region Help Classes & Functions
 | 
			
		||||
 | 
			
		||||
def _get_component_info(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int) -> UTIL_naming_convension.BallanceObjectInfo:
 | 
			
		||||
    match(comp_type):
 | 
			
		||||
        # process special for 2 unique components
 | 
			
		||||
        # process for 2 special unique components
 | 
			
		||||
        case PROP_ballance_element.BallanceElementType.PS_FourFlames:
 | 
			
		||||
            return UTIL_naming_convension.BallanceObjectInfo.create_from_others(UTIL_naming_convension.BallanceObjectType.LEVEL_START)
 | 
			
		||||
        case PROP_ballance_element.BallanceElementType.PE_Balloon:
 | 
			
		||||
@ -18,7 +53,10 @@ def _get_component_info(comp_type: PROP_ballance_element.BallanceElementType, co
 | 
			
		||||
            return UTIL_naming_convension.BallanceObjectInfo.create_from_resetpoint(comp_sector)
 | 
			
		||||
        # process for other components
 | 
			
		||||
        case _:
 | 
			
		||||
            return UTIL_naming_convension.BallanceObjectInfo.create_from_component(comp_type.name, comp_sector)
 | 
			
		||||
            return UTIL_naming_convension.BallanceObjectInfo.create_from_component(
 | 
			
		||||
                PROP_ballance_element.get_ballance_element_name(comp_type), 
 | 
			
		||||
                comp_sector
 | 
			
		||||
            )
 | 
			
		||||
    
 | 
			
		||||
def _set_component_by_info(obj: bpy.types.Object, info: UTIL_naming_convension.BallanceObjectInfo) -> None:
 | 
			
		||||
    # set component name and grouping it into virtools group at the same time
 | 
			
		||||
@ -56,6 +94,33 @@ def _check_component_existance(comp_type: PROP_ballance_element.BallanceElementT
 | 
			
		||||
    if expect_name in bpy.data.objects: return expect_name
 | 
			
		||||
    else: return None
 | 
			
		||||
 | 
			
		||||
def _general_create_component(comp_type: PROP_ballance_element.BallanceElementType, comp_sector: int, comp_count: int, comp_offset: typing.Callable[[int], mathutils.Matrix]) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    General component creation function.
 | 
			
		||||
 | 
			
		||||
    @param comp_type[in] The component type created.
 | 
			
		||||
    @param comp_sector[in] The sector param which passed to other functions. For non-sector component, pass any number.
 | 
			
		||||
    @param comp_count[in] The count of created component. For single component creation, please pass 1.
 | 
			
		||||
    @param comp_offset[in] The function pointer which receive 1 argument indicating the index of object which we want to get its offset.
 | 
			
		||||
        You can pass `lambda _: mathutils.Matrix.Identity(4)` to get zero offset for every items.
 | 
			
		||||
        You can pass `lambda _: mathutils.Matrix( xxx )` to get same offset for every items.
 | 
			
		||||
        You can pass `lambda i: mathutils.Matrix( func(i) )` to get index based offset for each items.
 | 
			
		||||
        The offset is the offset to the origin point, not the previous object.
 | 
			
		||||
    """
 | 
			
		||||
    # get element info first
 | 
			
		||||
    ele_info: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(comp_type, comp_sector)
 | 
			
		||||
    # create blc element context
 | 
			
		||||
    with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator:
 | 
			
		||||
        # object creation counter
 | 
			
		||||
        for i in range(comp_count):
 | 
			
		||||
            # get mesh from element context, and create with empty name first. we assign name later.
 | 
			
		||||
            obj: bpy.types.Object = bpy.data.objects.new('', creator.get_element(comp_type))
 | 
			
		||||
            # assign virtools group, object name by we gotten element info.
 | 
			
		||||
            _set_component_by_info(obj, ele_info)
 | 
			
		||||
            # add into scene and move to cursor
 | 
			
		||||
            UTIL_functions.add_into_scene_and_move_to_cursor(obj)
 | 
			
		||||
            # move with extra offset by calling offset getter
 | 
			
		||||
            obj.matrix_world = obj.matrix_world @ comp_offset(i)
 | 
			
		||||
        
 | 
			
		||||
class EnumPropHelper():
 | 
			
		||||
    """
 | 
			
		||||
@ -69,7 +134,7 @@ class EnumPropHelper():
 | 
			
		||||
                str(item.value), 
 | 
			
		||||
                item.name, 
 | 
			
		||||
                "", 
 | 
			
		||||
                UTIL_icons_manager.get_element_icon(item.name), 
 | 
			
		||||
                UTIL_icons_manager.get_element_icon(PROP_ballance_element.get_ballance_element_name(item)), 
 | 
			
		||||
                item.value
 | 
			
		||||
            ) for item in PROP_ballance_element.BallanceElementType
 | 
			
		||||
        )
 | 
			
		||||
@ -88,20 +153,14 @@ class EnumPropHelper():
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
class BBP_OT_add_component(bpy.types.Operator):
 | 
			
		||||
#region Noemal Component Adder
 | 
			
		||||
 | 
			
		||||
class BBP_OT_add_component(bpy.types.Operator, ComponentSectorParam):
 | 
			
		||||
    """Add Component"""
 | 
			
		||||
    bl_idname = "bbp.add_component"
 | 
			
		||||
    bl_label = "Add Component"
 | 
			
		||||
    bl_options = {'UNDO'}
 | 
			
		||||
 | 
			
		||||
    component_sector: bpy.props.IntProperty(
 | 
			
		||||
        name = "Sector",
 | 
			
		||||
        description = "Define which sector the object will be grouped in",
 | 
			
		||||
        min = 1, max = 999,
 | 
			
		||||
        soft_min = 1, soft_max = 8,
 | 
			
		||||
        default = 1,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    component_type: bpy.props.EnumProperty(
 | 
			
		||||
        name = "Type",
 | 
			
		||||
        description = "This component type",
 | 
			
		||||
@ -120,39 +179,255 @@ class BBP_OT_add_component(bpy.types.Operator):
 | 
			
		||||
        # only show sector for non-PE/PS component
 | 
			
		||||
        eletype: PROP_ballance_element.BallanceElementType = EnumPropHelper.get_selection(self.component_type)
 | 
			
		||||
        if eletype != PROP_ballance_element.BallanceElementType.PS_FourFlames and eletype != PROP_ballance_element.BallanceElementType.PE_Balloon:
 | 
			
		||||
            layout.prop(self, "component_sector")
 | 
			
		||||
            self.draw_component_sector_params(layout)
 | 
			
		||||
 | 
			
		||||
        # check for some special components and show warning
 | 
			
		||||
        elename: str | None = _check_component_existance(EnumPropHelper.get_selection(self.component_type), self.component_sector)
 | 
			
		||||
        elename: str | None = _check_component_existance(EnumPropHelper.get_selection(self.component_type), self.general_get_component_sector())
 | 
			
		||||
        if elename is not None:
 | 
			
		||||
            layout.label(text = f'Warning: {elename} already exist.')
 | 
			
		||||
 | 
			
		||||
    def execute(self, context):
 | 
			
		||||
        # create by ballance components
 | 
			
		||||
        eletype: PROP_ballance_element.BallanceElementType = EnumPropHelper.get_selection(self.component_type)
 | 
			
		||||
        eleinfo: UTIL_naming_convension.BallanceObjectInfo = _get_component_info(eletype, self.component_sector)
 | 
			
		||||
 | 
			
		||||
        with PROP_ballance_element.BallanceElementsHelper(bpy.context.scene) as creator:
 | 
			
		||||
            # create with empty name first
 | 
			
		||||
            obj = bpy.data.objects.new('', creator.get_element(eletype.value))
 | 
			
		||||
            # assign its props, including name
 | 
			
		||||
            _set_component_by_info(obj, eleinfo)
 | 
			
		||||
            # scene cursor
 | 
			
		||||
            UTIL_functions.add_into_scene_and_move_to_cursor(obj)
 | 
			
		||||
            
 | 
			
		||||
        # call general creator
 | 
			
		||||
        _general_create_component(
 | 
			
		||||
            EnumPropHelper.get_selection(self.component_type),
 | 
			
		||||
            self.general_get_component_sector(),
 | 
			
		||||
            1,  # only create one
 | 
			
		||||
            lambda _: mathutils.Matrix.Identity(4)
 | 
			
		||||
        )
 | 
			
		||||
        return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def draw_blc_menu(self, layout: bpy.types.UILayout):
 | 
			
		||||
        for item in PROP_ballance_element.BallanceElementType:
 | 
			
		||||
            item_name: str = PROP_ballance_element.get_ballance_element_name(item)
 | 
			
		||||
 | 
			
		||||
            cop = layout.operator(
 | 
			
		||||
                self.bl_idname, text = item.name, 
 | 
			
		||||
                icon_value = UTIL_icons_manager.get_element_icon(item.name))
 | 
			
		||||
                self.bl_idname, text = item_name, 
 | 
			
		||||
                icon_value = UTIL_icons_manager.get_element_icon(item_name)
 | 
			
		||||
            )
 | 
			
		||||
            cop.component_type = EnumPropHelper.to_selection(item)
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
#region Nong Comp Adder
 | 
			
		||||
 | 
			
		||||
class BBP_OT_add_nong_extra_point(bpy.types.Operator, ComponentSectorParam, ComponentCountParam):
 | 
			
		||||
    """Add Nong Extra Point"""
 | 
			
		||||
    bl_idname = "bbp.add_nong_extra_point"
 | 
			
		||||
    bl_label = "Nong Extra Point"
 | 
			
		||||
    bl_options = {'REGISTER', 'UNDO'}
 | 
			
		||||
 | 
			
		||||
    def draw(self, context):
 | 
			
		||||
        layout = self.layout
 | 
			
		||||
        self.draw_component_sector_params(layout)
 | 
			
		||||
        self.draw_component_count_params(layout)
 | 
			
		||||
 | 
			
		||||
    def execute(self, context):
 | 
			
		||||
        # create objects and rotate it by a certain degree calculated by its index
 | 
			
		||||
        # calc percent first
 | 
			
		||||
        percent: float = 1.0 / self.general_get_component_count()
 | 
			
		||||
        # create elements
 | 
			
		||||
        _general_create_component(
 | 
			
		||||
            PROP_ballance_element.BallanceElementType.P_Extra_Point,
 | 
			
		||||
            self.general_get_component_sector(),
 | 
			
		||||
            self.general_get_component_count(),
 | 
			
		||||
            lambda i: mathutils.Matrix.Rotation(percent * i * math.pi * 2, 4, 'Z')
 | 
			
		||||
        )
 | 
			
		||||
        return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def draw_blc_menu(self, layout: bpy.types.UILayout):
 | 
			
		||||
        layout.operator(
 | 
			
		||||
            BBP_OT_add_nong_extra_point.bl_idname,
 | 
			
		||||
            icon_value = UTIL_icons_manager.get_element_icon(
 | 
			
		||||
                PROP_ballance_element.get_ballance_element_name(PROP_ballance_element.BallanceElementType.P_Extra_Point)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
#region Series Comp Adder
 | 
			
		||||
 | 
			
		||||
class BBP_OT_add_tilting_block_series(bpy.types.Operator, ComponentSectorParam, ComponentCountParam):
 | 
			
		||||
    """Add Tilting Block Series"""
 | 
			
		||||
    bl_idname = "bbp.add_tilting_block_series"
 | 
			
		||||
    bl_label = "Tilting Block Series"
 | 
			
		||||
    bl_options = {'REGISTER', 'UNDO'}
 | 
			
		||||
 | 
			
		||||
    component_span: bpy.props.FloatProperty(
 | 
			
		||||
        name = "Span",
 | 
			
		||||
        description = "The distance between each titling blocks",
 | 
			
		||||
        min = 0.0, max = 100.0,
 | 
			
		||||
        soft_min = 0.0, soft_max = 12.0,
 | 
			
		||||
        default = 6.0022,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def draw(self, context):
 | 
			
		||||
        layout = self.layout
 | 
			
		||||
        self.draw_component_sector_params(layout)
 | 
			
		||||
        self.draw_component_count_params(layout)
 | 
			
		||||
        layout.prop(self, 'component_span')
 | 
			
		||||
 | 
			
		||||
    def execute(self, context):
 | 
			
		||||
        # create objects and move it by delta
 | 
			
		||||
        # get span first
 | 
			
		||||
        span: float = self.component_span
 | 
			
		||||
        # create elements
 | 
			
		||||
        _general_create_component(
 | 
			
		||||
            PROP_ballance_element.BallanceElementType.P_Modul_41,
 | 
			
		||||
            self.general_get_component_sector(),
 | 
			
		||||
            self.general_get_component_count(),
 | 
			
		||||
            lambda i: mathutils.Matrix.Translation(mathutils.Vector((span * i, 0.0, 0.0)))  # move with extra delta in x axis
 | 
			
		||||
        )
 | 
			
		||||
            
 | 
			
		||||
        return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def draw_blc_menu(self, layout: bpy.types.UILayout):
 | 
			
		||||
        layout.operator(
 | 
			
		||||
            BBP_OT_add_tilting_block_series.bl_idname,
 | 
			
		||||
            icon_value = UTIL_icons_manager.get_element_icon(
 | 
			
		||||
                PROP_ballance_element.get_ballance_element_name(PROP_ballance_element.BallanceElementType.P_Modul_41)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
class BBP_OT_add_ventilator_series(bpy.types.Operator, ComponentSectorParam, ComponentCountParam):
 | 
			
		||||
    """Add Ventilator Series"""
 | 
			
		||||
    bl_idname = "bbp.add_ventilator_series"
 | 
			
		||||
    bl_label = "Ventilator Series"
 | 
			
		||||
    bl_options = {'REGISTER', 'UNDO'}
 | 
			
		||||
 | 
			
		||||
    component_translation: bpy.props.FloatVectorProperty(
 | 
			
		||||
        name = "Delta Vector",
 | 
			
		||||
        description = "The translation between each ventilators. You can use this property to implement vertical or horizontal ventilator series. Set all factors to zero can get Nong ventilator.",
 | 
			
		||||
        size = 3, subtype = 'TRANSLATION',
 | 
			
		||||
        min = 0.0, max = 100.0,
 | 
			
		||||
        soft_min = 0.0, soft_max = 50.0,
 | 
			
		||||
        default = (0.0, 0.0, 15.0),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def draw(self, context):
 | 
			
		||||
        layout = self.layout
 | 
			
		||||
        self.draw_component_sector_params(layout)
 | 
			
		||||
        self.draw_component_count_params(layout)
 | 
			
		||||
        layout.prop(self, 'component_translation')
 | 
			
		||||
 | 
			
		||||
    def execute(self, context):
 | 
			
		||||
        # create objects and move it by delta
 | 
			
		||||
        # get translation first
 | 
			
		||||
        translation: mathutils.Vector = mathutils.Vector(self.component_translation)
 | 
			
		||||
        # create elements
 | 
			
		||||
        _general_create_component(
 | 
			
		||||
            PROP_ballance_element.BallanceElementType.P_Modul_18,
 | 
			
		||||
            self.general_get_component_sector(),
 | 
			
		||||
            self.general_get_component_count(),
 | 
			
		||||
            lambda i: mathutils.Matrix.Translation(i * translation)  # move with extra translation
 | 
			
		||||
        )
 | 
			
		||||
        
 | 
			
		||||
        return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def draw_blc_menu(self, layout: bpy.types.UILayout):
 | 
			
		||||
        layout.operator(
 | 
			
		||||
            BBP_OT_add_ventilator_series.bl_idname,
 | 
			
		||||
            icon_value = UTIL_icons_manager.get_element_icon(
 | 
			
		||||
                PROP_ballance_element.get_ballance_element_name(PROP_ballance_element.BallanceElementType.P_Modul_18)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
#region Comp Pair Adder
 | 
			
		||||
 | 
			
		||||
class BBP_OT_add_sector_component_pair(bpy.types.Operator, ComponentSectorParam):
 | 
			
		||||
    """Add Sector Pair, both check point and reset point."""
 | 
			
		||||
    bl_idname = "bbp.add_sector_component_pair"
 | 
			
		||||
    bl_label = "Sector Pair"
 | 
			
		||||
    bl_options = {'UNDO'}
 | 
			
		||||
 | 
			
		||||
    def __get_checkpoint(self) -> tuple[PROP_ballance_element.BallanceElementType, int]:
 | 
			
		||||
        if self.general_get_component_sector() == 1:
 | 
			
		||||
            return (PROP_ballance_element.BallanceElementType.PS_FourFlames, 1)
 | 
			
		||||
        else:
 | 
			
		||||
            # the sector of two flames should be `sector - 1` because first one was occupied by FourFlams
 | 
			
		||||
            return (PROP_ballance_element.BallanceElementType.PC_TwoFlames, self.general_get_component_sector() - 1)
 | 
			
		||||
 | 
			
		||||
    def __get_resetpoint(self) -> tuple[PROP_ballance_element.BallanceElementType, int]:
 | 
			
		||||
        # resetpoint's sector is just sector it self.
 | 
			
		||||
        return (PROP_ballance_element.BallanceElementType.PR_Resetpoint, self.general_get_component_sector())
 | 
			
		||||
 | 
			
		||||
    def invoke(self, context, event):
 | 
			
		||||
        wm = context.window_manager
 | 
			
		||||
        return wm.invoke_props_dialog(self)
 | 
			
		||||
 | 
			
		||||
    def draw(self, context):
 | 
			
		||||
        layout = self.layout
 | 
			
		||||
        self.draw_component_sector_params(layout)
 | 
			
		||||
 | 
			
		||||
        # check checkpoint and resetpoint name conflict and show warnings
 | 
			
		||||
        (checkp_ty, checkp_sector) = self.__get_checkpoint()
 | 
			
		||||
        elename: str | None = _check_component_existance(checkp_ty, checkp_sector)
 | 
			
		||||
        if elename is not None:
 | 
			
		||||
            layout.label(text = f'Warning: {elename} already exist.')
 | 
			
		||||
 | 
			
		||||
        (resetp_ty, resetp_sector) = self.__get_resetpoint()
 | 
			
		||||
        elename = _check_component_existance(resetp_ty, resetp_sector)
 | 
			
		||||
        if elename is not None:
 | 
			
		||||
            layout.label(text = f'Warning: {elename} already exist.')
 | 
			
		||||
 | 
			
		||||
    def execute(self, context):
 | 
			
		||||
        # create checkpoint and resetpoint individually in element context
 | 
			
		||||
        # get type and sector data first
 | 
			
		||||
        (checkp_ty, checkp_sector) = self.__get_checkpoint()
 | 
			
		||||
        (resetp_ty, resetp_sector) = self.__get_resetpoint()
 | 
			
		||||
        # calc resetpoint offset
 | 
			
		||||
        # resetpoint need a extra offset between checkpoint
 | 
			
		||||
        # but it is different in FourFlams and TwoFlams
 | 
			
		||||
        resetp_offset: float
 | 
			
		||||
        if checkp_ty == PROP_ballance_element.BallanceElementType.PS_FourFlames:
 | 
			
		||||
            resetp_offset = 3.25
 | 
			
		||||
        else:
 | 
			
		||||
            resetp_offset = 2.0
 | 
			
		||||
 | 
			
		||||
        # add elements
 | 
			
		||||
        # create checkpoint
 | 
			
		||||
        _general_create_component(
 | 
			
		||||
            checkp_ty,
 | 
			
		||||
            checkp_sector,
 | 
			
		||||
            1,  # only create one
 | 
			
		||||
            lambda _: mathutils.Matrix.Identity(4)
 | 
			
		||||
        )
 | 
			
		||||
        # create resetpoint
 | 
			
		||||
        _general_create_component(
 | 
			
		||||
            resetp_ty,
 | 
			
		||||
            resetp_sector,
 | 
			
		||||
            1,  # only create one
 | 
			
		||||
            lambda _: mathutils.Matrix.Translation(mathutils.Vector((0.0, 0.0, resetp_offset))) # apply resetpoint offset
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return {'FINISHED'}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def draw_blc_menu(self, layout: bpy.types.UILayout):
 | 
			
		||||
        layout.operator(
 | 
			
		||||
            BBP_OT_add_sector_component_pair.bl_idname,
 | 
			
		||||
            icon_value = UTIL_icons_manager.get_element_icon(
 | 
			
		||||
                PROP_ballance_element.get_ballance_element_name(PROP_ballance_element.BallanceElementType.PR_Resetpoint)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
def register():
 | 
			
		||||
    # register all classes
 | 
			
		||||
    bpy.utils.register_class(BBP_OT_add_component)
 | 
			
		||||
    bpy.utils.register_class(BBP_OT_add_nong_extra_point)
 | 
			
		||||
    bpy.utils.register_class(BBP_OT_add_tilting_block_series)
 | 
			
		||||
    bpy.utils.register_class(BBP_OT_add_ventilator_series)
 | 
			
		||||
    bpy.utils.register_class(BBP_OT_add_sector_component_pair)
 | 
			
		||||
 | 
			
		||||
def unregister():
 | 
			
		||||
    bpy.utils.unregister_class(BBP_OT_add_sector_component_pair)
 | 
			
		||||
    bpy.utils.unregister_class(BBP_OT_add_ventilator_series)
 | 
			
		||||
    bpy.utils.unregister_class(BBP_OT_add_tilting_block_series)
 | 
			
		||||
    bpy.utils.unregister_class(BBP_OT_add_nong_extra_point)
 | 
			
		||||
    bpy.utils.unregister_class(BBP_OT_add_component)
 | 
			
		||||
 | 
			
		||||
@ -35,26 +35,48 @@ class BallanceElementType(enum.IntEnum):
 | 
			
		||||
    PS_FourFlames = 26
 | 
			
		||||
 | 
			
		||||
_g_ElementCount: int = len(BallanceElementType)
 | 
			
		||||
_g_ElementNameIdMap: dict[str, int] = dict((entry.name, entry.value) for entry in BallanceElementType)
 | 
			
		||||
_g_ElementIdNameMap: dict[int, str] = dict((entry.value, entry.name) for entry in BallanceElementType)
 | 
			
		||||
 | 
			
		||||
def get_ballance_element_id(name: str) -> int | None:
 | 
			
		||||
def get_ballance_element_type_from_id(id: int) -> BallanceElementType | None:
 | 
			
		||||
    """
 | 
			
		||||
    Get Ballance element ID by its name.
 | 
			
		||||
    Get Ballance element type by its id.
 | 
			
		||||
 | 
			
		||||
    @param id[in] The id of element
 | 
			
		||||
    @return the type of this Ballance element name distributed by this plugin. or None if providing id is invalid.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return BallanceElementType(id)  # https://docs.python.org/zh-cn/3/library/enum.html#enum.EnumType.__call__
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
def get_ballance_element_type_from_name(name: str) -> BallanceElementType | None:
 | 
			
		||||
    """
 | 
			
		||||
    Get Ballance element type by its name.
 | 
			
		||||
 | 
			
		||||
    @param name[in] The name of element
 | 
			
		||||
    @return the ID of this Ballance element name distributed by this plugin. or None if providing name is invalid.
 | 
			
		||||
    @return the type of this Ballance element name distributed by this plugin. or None if providing name is invalid.
 | 
			
		||||
    """
 | 
			
		||||
    return _g_ElementNameIdMap.get(name, None)
 | 
			
		||||
    try:
 | 
			
		||||
        return BallanceElementType[name]    # https://docs.python.org/zh-cn/3/library/enum.html#enum.EnumType.__getitem__
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
def get_ballance_element_name(id: int) -> str | None:
 | 
			
		||||
def get_ballance_element_id(ty: BallanceElementType) -> int:
 | 
			
		||||
    """
 | 
			
		||||
    Get Ballance element name by its ID
 | 
			
		||||
    Get Ballance element id by its type
 | 
			
		||||
 | 
			
		||||
    @param id[in] The ID of element
 | 
			
		||||
    @return the name of this Ballance element, or None if ID is invalid.
 | 
			
		||||
    @param ty[in] The type of element
 | 
			
		||||
    @return the id of this Ballance element.
 | 
			
		||||
    """
 | 
			
		||||
    return _g_ElementIdNameMap.get(id, None)
 | 
			
		||||
    return ty.value
 | 
			
		||||
 | 
			
		||||
def get_ballance_element_name(ty: BallanceElementType) -> str:
 | 
			
		||||
    """
 | 
			
		||||
    Get Ballance element name by its type
 | 
			
		||||
 | 
			
		||||
    @param ty[in] The type of element
 | 
			
		||||
    @return the name of this Ballance element.
 | 
			
		||||
    """
 | 
			
		||||
    return ty.name
 | 
			
		||||
 | 
			
		||||
def is_ballance_element(name: str) -> bool:
 | 
			
		||||
    """
 | 
			
		||||
@ -65,16 +87,16 @@ def is_ballance_element(name: str) -> bool:
 | 
			
		||||
    @param name[in] The name of element
 | 
			
		||||
    @return True if providing name is Ballance element name.
 | 
			
		||||
    """
 | 
			
		||||
    return get_ballance_element_id(name) is not None
 | 
			
		||||
    return get_ballance_element_type_from_name(name) is not None
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
#region Ballance Elements Define & Visitor
 | 
			
		||||
 | 
			
		||||
class BBP_PG_ballance_element(bpy.types.PropertyGroup):
 | 
			
		||||
    element_name: bpy.props.StringProperty(
 | 
			
		||||
        name = "Element Name",
 | 
			
		||||
        default = ""
 | 
			
		||||
    element_id: bpy.props.IntProperty(
 | 
			
		||||
        name = "Element Id",
 | 
			
		||||
        default = 0
 | 
			
		||||
    )
 | 
			
		||||
    
 | 
			
		||||
    mesh_ptr: bpy.props.PointerProperty(
 | 
			
		||||
@ -94,12 +116,9 @@ def _save_element(mesh: bpy.types.Mesh, filename: str) -> None:
 | 
			
		||||
    # write this function and call this function in operator.
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
def _load_element(mesh: bpy.types.Mesh, element_id: int) -> None:
 | 
			
		||||
def _load_element(mesh: bpy.types.Mesh, element_type: BallanceElementType) -> None:
 | 
			
		||||
    # resolve mesh path
 | 
			
		||||
    element_name: str | None = get_ballance_element_name(element_id)
 | 
			
		||||
    if element_name is None:
 | 
			
		||||
        raise UTIL_functions.BBPException('invalid element id in _load_element()')
 | 
			
		||||
    
 | 
			
		||||
    element_name: str = get_ballance_element_name(element_type)
 | 
			
		||||
    element_filename: str = os.path.join(
 | 
			
		||||
        os.path.dirname(__file__),
 | 
			
		||||
        "meshes",
 | 
			
		||||
@ -208,7 +227,7 @@ class BallanceElementsHelper():
 | 
			
		||||
    __mSingletonMutex: typing.ClassVar[bool] = False
 | 
			
		||||
    __mIsValid: bool
 | 
			
		||||
    __mAssocScene: bpy.types.Scene
 | 
			
		||||
    __mElementMap: dict[int, bpy.types.Mesh]
 | 
			
		||||
    __mElementMap: dict[BallanceElementType, bpy.types.Mesh]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, assoc: bpy.types.Scene):
 | 
			
		||||
        self.__mElementMap = {}
 | 
			
		||||
@ -240,36 +259,30 @@ class BallanceElementsHelper():
 | 
			
		||||
            self.__mIsValid = False
 | 
			
		||||
            BallanceElementsHelper.__mSingletonMutex = False
 | 
			
		||||
    
 | 
			
		||||
    def get_element(self, element_id: int) -> bpy.types.Mesh:
 | 
			
		||||
    def get_element(self, element_type: BallanceElementType) -> 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)
 | 
			
		||||
        mesh: bpy.types.Mesh | None = self.__mElementMap.get(element_type, None)
 | 
			
		||||
        if mesh is not None: 
 | 
			
		||||
            return mesh
 | 
			
		||||
 | 
			
		||||
        # if no existing one, create new one
 | 
			
		||||
        new_mesh_name: str | None = get_ballance_element_name(element_id)
 | 
			
		||||
        if new_mesh_name is None: 
 | 
			
		||||
            raise UTIL_functions.BBPException('invalid element id')
 | 
			
		||||
        new_mesh_name: str = get_ballance_element_name(element_type)
 | 
			
		||||
        new_mesh: bpy.types.Mesh = bpy.data.meshes.new(new_mesh_name)
 | 
			
		||||
 | 
			
		||||
        _load_element(new_mesh, element_id)
 | 
			
		||||
        self.__mElementMap[element_id] = new_mesh
 | 
			
		||||
        _load_element(new_mesh, element_type)
 | 
			
		||||
        self.__mElementMap[element_type] = new_mesh
 | 
			
		||||
        return new_mesh
 | 
			
		||||
 | 
			
		||||
    def __write_to_ballance_elements(self) -> None:
 | 
			
		||||
        elements: bpy.types.CollectionProperty = get_ballance_elements(self.__mAssocScene)
 | 
			
		||||
        elements.clear()
 | 
			
		||||
 | 
			
		||||
        for eleid, elemesh in self.__mElementMap.items():
 | 
			
		||||
            name: str | None = get_ballance_element_name(eleid)
 | 
			
		||||
            if name is None:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        for elety, elemesh in self.__mElementMap.items():
 | 
			
		||||
            item: BBP_PG_ballance_element = elements.add()
 | 
			
		||||
            item.element_name = name
 | 
			
		||||
            item.element_id = get_ballance_element_id(elety)
 | 
			
		||||
            item.mesh_ptr = elemesh
 | 
			
		||||
 | 
			
		||||
    def __read_from_ballance_element(self) -> None:
 | 
			
		||||
@ -280,11 +293,11 @@ class BallanceElementsHelper():
 | 
			
		||||
        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
 | 
			
		||||
            element_type: BallanceElementType | None = get_ballance_element_type_from_id(item.element_id)
 | 
			
		||||
            if element_type is None: continue
 | 
			
		||||
 | 
			
		||||
            # add into map
 | 
			
		||||
            self.__mElementMap[mesh_id] = item.mesh_ptr
 | 
			
		||||
            self.__mElementMap[element_type] = item.mesh_ptr
 | 
			
		||||
 | 
			
		||||
def reset_ballance_elements(scene: bpy.types.Scene) -> None:
 | 
			
		||||
    invalid_idx: list[int] = []
 | 
			
		||||
@ -294,13 +307,13 @@ def reset_ballance_elements(scene: bpy.types.Scene) -> None:
 | 
			
		||||
    index: int = 0
 | 
			
		||||
    item: BBP_PG_ballance_element
 | 
			
		||||
    for item in elements:
 | 
			
		||||
        eleid: int | None = get_ballance_element_id(item.element_name)
 | 
			
		||||
        elety: BallanceElementType | None = get_ballance_element_type_from_id(item.element_id)
 | 
			
		||||
 | 
			
		||||
        # load or record invalid entry
 | 
			
		||||
        if eleid is None or item.mesh_ptr is None:
 | 
			
		||||
        if elety is None or item.mesh_ptr is None:
 | 
			
		||||
            invalid_idx.append(index)
 | 
			
		||||
        else:
 | 
			
		||||
            _load_element(item.mesh_ptr, eleid)
 | 
			
		||||
            _load_element(item.mesh_ptr, elety)
 | 
			
		||||
 | 
			
		||||
        # inc counter
 | 
			
		||||
        index += 1
 | 
			
		||||
@ -316,8 +329,12 @@ def reset_ballance_elements(scene: bpy.types.Scene) -> None:
 | 
			
		||||
 | 
			
		||||
class BBP_UL_ballance_elements(bpy.types.UIList):
 | 
			
		||||
    def draw_item(self, context, layout: bpy.types.UILayout, data, item: BBP_PG_ballance_element, icon, active_data, active_propname):
 | 
			
		||||
        if item.element_name != "" and item.mesh_ptr is not None:
 | 
			
		||||
            layout.label(text = item.element_name, translate = False)
 | 
			
		||||
        # check requirements
 | 
			
		||||
        elety: BallanceElementType | None = get_ballance_element_type_from_id(item.element_id)
 | 
			
		||||
        if elety is None or item.mesh_ptr is None: return
 | 
			
		||||
 | 
			
		||||
        # draw list item
 | 
			
		||||
        layout.label(text = get_ballance_element_name(elety), translate = False)
 | 
			
		||||
        layout.label(text = item.mesh_ptr.name, translate = False, icon = 'MESH_DATA')
 | 
			
		||||
 | 
			
		||||
class BBP_OT_reset_ballance_elements(bpy.types.Operator):
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import bpy
 | 
			
		||||
import bpy, mathutils
 | 
			
		||||
import math, typing, enum, sys
 | 
			
		||||
 | 
			
		||||
class BBPException(Exception):
 | 
			
		||||
@ -53,11 +53,17 @@ def message_box(message: tuple[str, ...], title: str, icon: str):
 | 
			
		||||
    bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
 | 
			
		||||
 | 
			
		||||
def move_to_cursor(obj: bpy.types.Object):
 | 
			
		||||
    obj.location = bpy.context.scene.cursor.location
 | 
			
		||||
    # use obj.matrix_world to move, not obj.location because this bug:
 | 
			
		||||
    # https://blender.stackexchange.com/questions/27667/incorrect-matrix-world-after-transformation
 | 
			
		||||
    # the update of matrix_world after setting location is not immediately.
 | 
			
		||||
    # and calling update() function for view_layer for the translation of each object is not suit for too much objects.
 | 
			
		||||
 | 
			
		||||
    # obj.location = bpy.context.scene.cursor.location
 | 
			
		||||
    obj.matrix_world = obj.matrix_world @ mathutils.Matrix.Translation(bpy.context.scene.cursor.location - obj.location)
 | 
			
		||||
 | 
			
		||||
def add_into_scene_and_move_to_cursor(obj: bpy.types.Object):
 | 
			
		||||
    move_to_cursor(obj)
 | 
			
		||||
 | 
			
		||||
    view_layer = bpy.context.view_layer
 | 
			
		||||
    collection = view_layer.active_layer_collection.collection
 | 
			
		||||
    collection.objects.link(obj)
 | 
			
		||||
 | 
			
		||||
    move_to_cursor(obj)
 | 
			
		||||
 | 
			
		||||
@ -67,23 +67,28 @@ class BBP_MT_AddRailMenu(bpy.types.Menu):
 | 
			
		||||
 | 
			
		||||
    def draw(self, context):
 | 
			
		||||
        layout = self.layout
 | 
			
		||||
class BBP_MT_AddElementsMenu(bpy.types.Menu):
 | 
			
		||||
    """Add Ballance Elements"""
 | 
			
		||||
    bl_idname = "BBP_MT_AddElementsMenu"
 | 
			
		||||
    bl_label = "Elements"
 | 
			
		||||
class BBP_MT_AddComponentsMenu(bpy.types.Menu):
 | 
			
		||||
    """Add Ballance Components"""
 | 
			
		||||
    bl_idname = "BBP_MT_AddComponentsMenu"
 | 
			
		||||
    bl_label = "Components"
 | 
			
		||||
    def draw(self, context):
 | 
			
		||||
        layout = self.layout
 | 
			
		||||
 | 
			
		||||
        layout.label(text="Basic Elements")
 | 
			
		||||
        layout.label(text="Basic Components")
 | 
			
		||||
        OP_ADDS_component.BBP_OT_add_component.draw_blc_menu(layout)
 | 
			
		||||
        
 | 
			
		||||
        layout.separator()
 | 
			
		||||
        layout.label(text="Duplicated Elements")
 | 
			
		||||
        #OBJS_add_components.BBP_OT_add_components_dup.draw_blc_menu(layout)
 | 
			
		||||
        layout.label(text="Nong Components")
 | 
			
		||||
        OP_ADDS_component.BBP_OT_add_nong_extra_point.draw_blc_menu(layout)
 | 
			
		||||
 | 
			
		||||
        layout.separator()
 | 
			
		||||
        layout.label(text="Elements Pair")
 | 
			
		||||
        #OBJS_add_components.BBP_OT_add_components_series.draw_blc_menu(layout)
 | 
			
		||||
        layout.label(text="Series Components")
 | 
			
		||||
        OP_ADDS_component.BBP_OT_add_tilting_block_series.draw_blc_menu(layout)
 | 
			
		||||
        OP_ADDS_component.BBP_OT_add_ventilator_series.draw_blc_menu(layout)
 | 
			
		||||
 | 
			
		||||
        layout.separator()
 | 
			
		||||
        layout.label(text="Components Pair")
 | 
			
		||||
        OP_ADDS_component.BBP_OT_add_sector_component_pair.draw_blc_menu(layout)
 | 
			
		||||
 | 
			
		||||
# ===== Menu Drawer =====
 | 
			
		||||
 | 
			
		||||
@ -109,10 +114,10 @@ def menu_drawer_add(self, context):
 | 
			
		||||
    layout.label(text="Ballance")
 | 
			
		||||
    layout.menu(BBP_MT_AddFloorMenu.bl_idname, icon='MESH_CUBE')
 | 
			
		||||
    layout.menu(BBP_MT_AddRailMenu.bl_idname, icon='MESH_CIRCLE')
 | 
			
		||||
    layout.menu(BBP_MT_AddElementsMenu.bl_idname, icon='MESH_ICOSPHERE')
 | 
			
		||||
    layout.menu(BBP_MT_AddComponentsMenu.bl_idname, icon='MESH_ICOSPHERE')
 | 
			
		||||
    #layout.operator_menu_enum(
 | 
			
		||||
    #    OBJS_add_components.BALLANCE_OT_add_components.bl_idname, 
 | 
			
		||||
    #    "elements_type", icon='MESH_ICOSPHERE', text="Elements")
 | 
			
		||||
    #    "Components_type", icon='MESH_ICOSPHERE', text="Components")
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
#region Register and Unregister.
 | 
			
		||||
@ -121,7 +126,7 @@ g_BldClasses: tuple[typing.Any, ...] = (
 | 
			
		||||
    BBP_MT_View3DMenu,
 | 
			
		||||
    BBP_MT_AddFloorMenu,
 | 
			
		||||
    BBP_MT_AddRailMenu,
 | 
			
		||||
    BBP_MT_AddElementsMenu
 | 
			
		||||
    BBP_MT_AddComponentsMenu
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
class MenuEntry():
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user