import bpy
import bmesh
def apply_boolean_mirror(arg_objectname="Default") -> bool:
"""ブーリアンモディファイアを使って対象オブジェクトをX軸で分割する
その後、ミラーモディファイアでX軸対称のオブジェクトとする
Keyword Arguments:
arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
Returns:
bool -- 実行正否
"""
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return False
if selectob.type != 'MESH':
return False
boolean_objname = "Difference"
diff_shift = 0.0001
cleanup_threshold = diff_shift * 10
delzero_threshold = cleanup_threshold * 2
mirror_threshold = delzero_threshold * 2
trans_result = transform_apply_target(arg_objectname=arg_objectname)
if trans_result != True:
return False
make_result = make_cube_difference(arg_objectname=arg_objectname, arg_makename=boolean_objname, arg_shift=diff_shift)
if make_result != True:
return False
apply_result = apply_boolean_difference(arg_objectname=arg_objectname, arg_diffname=boolean_objname)
if apply_result != True:
return False
cleanup_result = cleanup_mesh_object(arg_objectname=arg_objectname, arg_threshold=cleanup_threshold)
if cleanup_result != True:
return False
delzero_result = delete_face_xzero(arg_objectname=arg_objectname, arg_threshold=delzero_threshold)
if delzero_result != True:
return False
mirror_result = apply_mirror_Xclipping(arg_objectname=arg_objectname, arg_threshold=mirror_threshold)
if mirror_result != True:
return False
return True
def transform_apply_target(arg_objectname="Default") -> bool:
"""対象オブジェクトのトランスフォームを適用する
Keyword Arguments:
arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
Returns:
bool -- 実行の正否
"""
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return False
for ob in bpy.context.scene.objects:
ob.select_set(False)
selectob.select_set(True)
bpy.context.view_layer.objects.active = selectob
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
return True
def make_cube_difference(arg_objectname="Default", arg_makename="Difference", arg_shift=0.0) -> bool:
"""ブーリアンモディファイアの差分検出用Cubeを作成する
Keyword Arguments:
arg_objectname {str} -- 指定オブジェクト名 (default: {"Default"})
arg_makename {str} -- 作成オブジェクト名 (default: {"Difference"})
arg_shift {float} -- ずらし (default: {0.0})
Returns:
bool -- 実行正否
"""
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return False
target_boundbox = selectob.bound_box
vertcount = 0
center_x = 0.0
center_y = 0.0
center_z = 0.0
for vert_boundbox in target_boundbox:
center_x += vert_boundbox[0]
center_y += vert_boundbox[1]
center_z += vert_boundbox[2]
vertcount += 1
center_x = center_x / vertcount
center_y = center_y / vertcount
center_z = center_z / vertcount
dimension_x = selectob.dimensions[0]
dimension_y = selectob.dimensions[1]
dimension_z = selectob.dimensions[2]
if arg_makename in bpy.data.objects:
return False
bpy.ops.mesh.primitive_cube_add(
size=2.0,
location=(0.0, 0.0, 0.0),
rotation=(0.0, 0.0, 0.0)
)
makeob = bpy.context.view_layer.objects.active
makeob.name = arg_makename
makeob.location = (center_x, center_y, center_z)
makeob.scale = ((dimension_x + 1.0) / 2.0, (dimension_y + 1.0) / 2.0, (dimension_z + 1.0) / 2.0)
makeob.location[0] = -1.0 * ((dimension_x + 1.0) / 2.0)
makeob.location[0] = makeob.location[0] + arg_shift
return True
def apply_boolean_difference(arg_objectname="Default", arg_diffname="Difference") -> bool:
"""対象オブジェクトにブーリアンモディファイアの差分を適用する
差分に用いたオブジェクトは削除する
Keyword Arguments:
arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
arg_diffname {str} -- 差分オブジェクト名 (default: {"Difference"})
Returns:
bool -- 実行正否
"""
diffob = bpy.data.objects.get(arg_diffname)
if diffob == None:
return False
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return False
bpy.context.view_layer.objects.active = selectob
bpy.ops.object.modifier_add(type='BOOLEAN')
boolean_modifier = selectob.modifiers[-1]
boolean_modifier.operation = 'DIFFERENCE'
boolean_modifier.object = diffob
boolean_modifier.double_threshold = 0.000001
bpy.ops.object.modifier_apply(apply_as='DATA',modifier=boolean_modifier.name)
bpy.data.objects.remove(diffob)
return True
def cleanup_mesh_object(arg_objectname="Default", arg_threshold=0.0001) -> bool:
"""オブジェクトにクリーンアップを実行
・大きさ0を融解:面積が0の面を削除し、1つの頂点にまとめる
・孤立を削除:どの面にもつながっていない辺や頂点を削除する
・重複頂点を削除:重複している頂点を1つの頂点にまとめる
Keyword Arguments:
arg_objectname {str} -- 指定オブジェクト名 (default: {"Default"})
arg_threshold {float} -- 結合しきい値 (default: {0.0001})
Returns:
bool -- 実行の正否
"""
for ob in bpy.context.scene.objects:
ob.select_set(False)
targetob = bpy.data.objects.get(arg_objectname)
if targetob == None:
return False
bpy.context.view_layer.objects.active = targetob
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.dissolve_degenerate(threshold=0.01)
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.delete_loose(use_verts=True, use_edges=True, use_faces=False)
bpy.ops.mesh.select_all()
bpy.ops.mesh.remove_doubles(threshold=arg_threshold, use_unselected=False)
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
return True
def delete_face_xzero(arg_objectname="Default", arg_threshold=0.0001) -> bool:
"""X軸ゼロにある面を削除する
Keyword Arguments:
arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
arg_threshold {float} -- 選択しきい値 (default: {0.0001})
Returns:
bool -- 実行正否
"""
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return False
for ob in bpy.context.scene.objects:
ob.select_set(False)
selectob.select_set(True)
bpy.context.view_layer.objects.active = selectob
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
meshdata = bmesh.from_edit_mesh(selectob.data)
meshdata.select_mode = {'VERT'}
bpy.ops.mesh.select_all(action='DESELECT')
for v in meshdata.verts:
if v.co.x < arg_threshold:
v.select_set(True)
else:
v.select_set(False)
meshdata.select_flush_mode()
bpy.ops.mesh.delete(type='FACE')
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
return True
def apply_mirror_Xclipping(arg_objectname="Default", arg_threshold=0.0001) -> bool:
"""対象オブジェクトにブーリアンモディファイアの差分を適用する
Keyword Arguments:
arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
arg_threshold {float} -- 結合しきい値 (default: {0.0001})
Returns:
bool -- 実行正否
"""
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return False
bpy.context.view_layer.objects.active = selectob
bpy.ops.object.modifier_add(type='MIRROR')
mirror_modifier = selectob.modifiers[-1]
mirror_modifier.use_axis[0] = True
mirror_modifier.use_axis[1] = False
mirror_modifier.use_axis[2] = False
mirror_modifier.use_mirror_merge = True
mirror_modifier.use_clip = True
mirror_modifier.use_mirror_vertex_groups = True
mirror_modifier.merge_threshold = arg_threshold
bpy.ops.object.modifier_apply(apply_as='DATA',modifier=mirror_modifier.name)
return True
def get_object_center(arg_objectname="Default") -> list:
"""オブジェクトのバウンドボックスから中点を計算する
Keyword Arguments:
arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
Returns:
list -- 中点座標
"""
selectob = bpy.data.objects.get(arg_objectname)
if selectob == None:
return [0.0, 0.0, 0.0]
target_boundbox = selectob.bound_box
vertcount = 0
center_x = 0.0
center_y = 0.0
center_z = 0.0
for vert_boundbox in target_boundbox:
center_x += vert_boundbox[0]
center_y += vert_boundbox[1]
center_z += vert_boundbox[2]
vertcount += 1
center_x = center_x / vertcount
center_y = center_y / vertcount
center_z = center_z / vertcount
return [center_x, center_y, center_z]
for mesh in bpy.data.objects:
if mesh.type == 'MESH':
apply_boolean_mirror(arg_objectname=mesh.name)