Sidebar(N) > HoleMaker", "description": "Generates a hollowed sphere with uniformly distributed holes.", "category": "Object", } class HoleMakerProperties(bpy.types.PropertyGroup): sphere_radius: bpy.props.FloatProperty( name="球体の半径", default=1.0, min=0.1 ) thickness: bpy.props.FloatProperty( name="球体の厚み (空洞化)", description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します", default=0.01, min=0.001 ) cylinder_radius: bpy.props.FloatProperty( name="円柱の半径", default=0.1, min=0.01 ) cylinder_count: bpy.props.IntProperty( name="円柱の本数", default=12, min=1 ) class OBJECT_OT_hole_maker(bpy.types.Operator): bl_idname = "object.hole_maker" bl_label = "穴あき中空球体を生成" bl_options = {'REGISTER"> Sidebar(N) > HoleMaker", "description": "Generates a hollowed sphere with uniformly distributed holes.", "category": "Object", } class HoleMakerProperties(bpy.types.PropertyGroup): sphere_radius: bpy.props.FloatProperty( name="球体の半径", default=1.0, min=0.1 ) thickness: bpy.props.FloatProperty( name="球体の厚み (空洞化)", description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します", default=0.01, min=0.001 ) cylinder_radius: bpy.props.FloatProperty( name="円柱の半径", default=0.1, min=0.01 ) cylinder_count: bpy.props.IntProperty( name="円柱の本数", default=12, min=1 ) class OBJECT_OT_hole_maker(bpy.types.Operator): bl_idname = "object.hole_maker" bl_label = "穴あき中空球体を生成" bl_options = {'REGISTER"> Sidebar(N) > HoleMaker", "description": "Generates a hollowed sphere with uniformly distributed holes.", "category": "Object", } class HoleMakerProperties(bpy.types.PropertyGroup): sphere_radius: bpy.props.FloatProperty( name="球体の半径", default=1.0, min=0.1 ) thickness: bpy.props.FloatProperty( name="球体の厚み (空洞化)", description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します", default=0.01, min=0.001 ) cylinder_radius: bpy.props.FloatProperty( name="円柱の半径", default=0.1, min=0.01 ) cylinder_count: bpy.props.IntProperty( name="円柱の本数", default=12, min=1 ) class OBJECT_OT_hole_maker(bpy.types.Operator): bl_idname = "object.hole_maker" bl_label = "穴あき中空球体を生成" bl_options = {'REGISTER">
import bpy
import math
import mathutils

bl_info = {
    "name": "HoleMaker",
    "author": "AI",
    "version": (1, 2),
    "blender": (4, 0, 0),
    "location": "View3D > Sidebar(N) > HoleMaker",
    "description": "Generates a hollowed sphere with uniformly distributed holes.",
    "category": "Object",
}

class HoleMakerProperties(bpy.types.PropertyGroup):
    sphere_radius: bpy.props.FloatProperty(
        name="球体の半径",
        default=1.0,
        min=0.1
    )
    thickness: bpy.props.FloatProperty(
        name="球体の厚み (空洞化)",
        description="メイン球体の半径からこの値を引いたサイズの球体で内部を空洞化します",
        default=0.01,
        min=0.001
    )
    cylinder_radius: bpy.props.FloatProperty(
        name="円柱の半径",
        default=0.1,
        min=0.01
    )
    cylinder_count: bpy.props.IntProperty(
        name="円柱の本数",
        default=12,
        min=1
    )

class OBJECT_OT_hole_maker(bpy.types.Operator):
    bl_idname = "object.hole_maker"
    bl_label = "穴あき中空球体を生成"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        props = context.scene.hole_maker_props
        
        # 1. 選択状態をクリア
        bpy.ops.object.select_all(action='DESELECT')
        
        # 2. ベースとなる球体(外側)を作成
        bpy.ops.mesh.primitive_uv_sphere_add(
            radius=props.sphere_radius, 
            location=(0, 0, 0)
        )
        sphere = context.active_object
        
        # 3. 内部空洞化の処理
        inner_radius = props.sphere_radius - props.thickness
        if inner_radius > 0:
            # くり抜き用の内側の球体を作成
            bpy.ops.mesh.primitive_uv_sphere_add(
                radius=inner_radius,
                location=(0, 0, 0)
            )
            inner_sphere = context.active_object
            
            # ブーリアンを適用するために外側の球体をアクティブにする
            context.view_layer.objects.active = sphere
            
            # 空洞化用ブーリアンモディファイアの設定
            mod_hollow = sphere.modifiers.new(name="HollowBoolean", type='BOOLEAN')
            mod_hollow.operation = 'DIFFERENCE'
            try:
                mod_hollow.solver = 'FAST'
            except TypeError:
                mod_hollow.solver = 'FLOAT'
            mod_hollow.object = inner_sphere
            
            # モディファイアを適用
            bpy.ops.object.modifier_apply(modifier=mod_hollow.name)
            
            # くり抜きに使った内側の球体を削除
            bpy.data.objects.remove(inner_sphere, do_unlink=True)
        
        # 4. 円柱の生成と貫通穴のブーリアン処理
        n = props.cylinder_count
        phi = math.pi * (3.0 - math.sqrt(5.0)) # フィボナッチ黄金角
        cylinder_depth = props.sphere_radius * 2.5 
        
        for i in range(n):
            y = 1.0 - (i / float(n - 1)) * 2.0 if n > 1 else 0.0
            radius = math.sqrt(1.0 - y * y)
            theta = phi * i
            
            x = math.cos(theta) * radius
            z = math.sin(theta) * radius
            
            vec = mathutils.Vector((x, y, z))
            rot = vec.to_track_quat('Z', 'Y').to_euler()
            
            bpy.ops.mesh.primitive_cylinder_add(
                radius=props.cylinder_radius,
                depth=cylinder_depth,
                location=(0, 0, 0),
                rotation=rot
            )
            cylinder = context.active_object
            
            context.view_layer.objects.active = sphere
            
            mod = sphere.modifiers.new(name="HoleBoolean", type='BOOLEAN')
            mod.operation = 'DIFFERENCE'
            try:
                mod.solver = 'FAST'
            except TypeError:
                mod.solver = 'FLOAT'
            mod.object = cylinder
            
            bpy.ops.object.modifier_apply(modifier=mod.name)
            bpy.data.objects.remove(cylinder, do_unlink=True)
            
        # 処理完了後、生成した球体を選択・アクティブ状態にする
        sphere.select_set(True)
        context.view_layer.objects.active = sphere
        
        return {'FINISHED'}

class VIEW3D_PT_hole_maker(bpy.types.Panel):
    bl_label = "HoleMaker"
    bl_idname = "VIEW3D_PT_hole_maker"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'HoleMaker'

    def draw(self, context):
        layout = self.layout
        props = context.scene.hole_maker_props
        
        # UIプロパティの配置
        col = layout.column(align=True)
        col.prop(props, "sphere_radius")
        col.prop(props, "thickness") # 厚み(0.01)を調整できるパラメーター
        col.prop(props, "cylinder_radius")
        col.prop(props, "cylinder_count")
        
        layout.separator()
        
        # 実行ボタンの配置
        layout.operator("object.hole_maker", text="穴あき中空球体を生成", icon='MESH_UVSPHERE')

classes = (
    HoleMakerProperties,
    OBJECT_OT_hole_maker,
    VIEW3D_PT_hole_maker,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.hole_maker_props = bpy.props.PointerProperty(type=HoleMakerProperties)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.hole_maker_props

if __name__ == "__main__":
    register()