

https://aistudio.google.com/app/prompts?state={"ids":["1ErHJKWdIaYjz5pQuhxclDnCIGyuxE04K"],"action":"open","userId":"100872131270251692722","resourceKeys":{}}&usp=sharing
# blender-cod: utf-8
# ---------------------------------------------------------------------
# アドオン基本情報
# ---------------------------------------------------------------------
bl_info = {
"name": "369 緯度経度 球体 (ido_keido_001)",
"author": "あなたのお名前 & zionadchat",
"version": (3, 7, 3), # ### 変更 ###: 画像の繰り返し貼付オプションを追加
"blender": (4, 1, 0),
"location": "View3D > Sidebar > [ 単体 緯度経度 ]",
"description": "緯線・経線を生成。球体表面の特定経度・緯度範囲のハイライトを個別に追加作成し、表裏に別々の画像を貼る機能を持ちます。",
"category": "Object",
}
import bpy
import webbrowser
import math
import mathutils
import os
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, FloatProperty, FloatVectorProperty, PointerProperty, EnumProperty, BoolProperty, IntProperty
# ---------------------------------------------------------------------
# アドオン設定
# ---------------------------------------------------------------------
PREFIX = "ido_keido_001"
ADDON_CATEGORY_NAME = "[ 単体 緯度経度 ]"
# ---------------------------------------------------------------------
# アドオンの初期設定値
# ---------------------------------------------------------------------
addon_defaults = {
"axis_reference": 'Z', "lalo_radius": 10.0,
"latitude_thickness": 0.30,
"longitude_thickness": 0.10,
"latitude_color": (0.8, 0.0, 0.8, 1.0), "longitude_color": (0.5, 1.0, 0.0, 1.0),
"latitude_creation_mode": 'ANGLE', "latitude_divisions": 5, "latitude_object_name": "Latitude",
"latitude_angle_step": math.radians(30),
"longitude_creation_mode": 'ANGLE', "longitude_divisions": 11, "longitude_object_name": "Longitude",
"longitude_angle_step": math.radians(30),
"torus_plane": 'XZ', "torus_major_radius": 10.0, "torus_minor_radius": 0.25, "torus_color": (0.2, 0.8, 1.0, 0.1),
"torus_sub_collection_name": "TorusGuide", "torus_object_name": "GuideTorus",
"use_collections": True, "collection_name": "RayGenesis_Objects",
"latitude_sub_collection_name": "Latitudes", "longitude_sub_collection_name": "Longitudes",
"join_torus_guides": False,
"join_latitudes": False,
"join_longitudes": False,
"join_latlon_together": False,
"highlight_sphere_color": (0.0, 0.5, 1.0, 0.1),
"highlight_sphere_name": "HighlightSurface",
"highlight_sub_collection_name": "HighlightSpheres",
"highlight_sphere_start_lon": 0.0,
"highlight_sphere_end_lon": math.radians(180.0),
"highlight_sphere_start_lat": math.radians(-90.0),
"highlight_sphere_end_lat": math.radians(90.0),
"highlight_mode": 'COLOR',
"highlight_front_image": "",
"highlight_back_image": "",
# ### 追加 ###: 画像変形用の初期値
"highlight_front_image_scale": 1.0,
"highlight_front_image_rotation": 0.0,
"highlight_front_image_flip_x": False,
"highlight_back_image_scale": 1.0,
"highlight_back_image_rotation": 0.0,
"highlight_back_image_flip_x": False,
# ### 新規追加 ###: 画像マッピング方法の初期値
"highlight_front_image_mapping": 'CLIP',
"highlight_back_image_mapping": 'CLIP',
}
# ---------------------------------------------------------------------
# リンクデータ (変更なし)
# ---------------------------------------------------------------------
THIS_LINKS = [{"label": "円錐矢印 20251014", "url": "<https://memo2017.hatenlog.com/entry/2025/10/14/090711>"}]
ADDON_LINKS = [{"label": "blender 2026", "url": "<https://www.notion.so/blender-2026-280b3deba7a280499f3dc81c81f588e3"},{"label>": "立方体と3球体 20250926", "url": "<https://memo2017.hatenlog.com/entry/2025/09/27/000842"},{"label>": "blender アドオン 公開", "url": "<https://ivory-handsaw-95b.notion.site/blender-230b3deba7a280d7b610e0e3cdc178da>"}]
NEW_DOC_LINKS = [{"label": "完成品 目次", "url": "<https://mokuji000zionad.hatenablog.com/entry/2025/05/30/135936>"}]
DOC_LINKS = [{"label": "812 地球儀 経度 緯度でのコントロール 20250302", "url": "<https://sortphotos2025.hatenablog.jp/entry/2025/03/02/211757>"}]
SOCIAL_LINKS = [{"label": "X (Twitter) zionadchat", "url": "<https://x.com/zionadchat>"}]
# ---------------------------------------------------------------------
# 共通ユーティリティ関数群
# ---------------------------------------------------------------------
def get_or_create_collection(collection_name, parent_collection=None):
if parent_collection is None: parent_collection = bpy.context.scene.collection
if collection_name in parent_collection.children: return parent_collection.children[collection_name]
else: new_collection = bpy.data.collections.new(collection_name); parent_collection.children.link(new_collection); return new_collection
def link_to_collection(obj, collection):
for coll in obj.users_collection: coll.objects.unlink(obj)
collection.objects.link(obj)
def get_or_create_material(color, name_prefix="RayGenesis_Color"):
mat_name = f"{name_prefix}_{color[0]:.2f}_{color[1]:.2f}_{color[2]:.2f}_{color[3]:.2f}"
if mat_name in bpy.data.materials: return bpy.data.materials[mat_name]
mat = bpy.data.materials.new(name=mat_name); mat.use_nodes = True; bsdf = mat.node_tree.nodes.get('Principled BSDF')
if bsdf: bsdf.inputs['Base Color'].default_value = color; bsdf.inputs['Alpha'].default_value = color[3]; mat.blend_method = 'BLEND' if color[3] < 1.0 else 'OPAQUE'
return mat
# ### 変更 ###: 画像の繰り返し設定を追加
def _create_highlight_image_material(mat_name, color,
front_image_path, front_scale, front_rotation, front_flip_x, front_mapping,
back_image_path, back_scale, back_rotation, back_flip_x, back_mapping):
use_front_img = front_image_path and os.path.exists(bpy.path.abspath(front_image_path))
use_back_img = back_image_path and os.path.exists(bpy.path.abspath(back_image_path))
if not use_front_img and not use_back_img:
return get_or_create_material(color, "HighlightSurface_Fallback")
if mat_name in bpy.data.materials:
bpy.data.materials.remove(bpy.data.materials[mat_name])
mat = bpy.data.materials.new(name=mat_name)
mat.use_nodes = True
mat.blend_method = 'BLEND'
nodes = mat.node_tree.nodes
links = mat.node_tree.links
nodes.clear()
output = nodes.new(type='ShaderNodeOutputMaterial'); output.location = (400, 0)
mix_shader = nodes.new(type='ShaderNodeMixShader'); mix_shader.location = (200, 0)
geom = nodes.new(type='ShaderNodeNewGeometry'); geom.location = (0, -100)
tex_coord = nodes.new(type='ShaderNodeTexCoord'); tex_coord.location = (-1000, 0)
links.new(geom.outputs['Backfacing'], mix_shader.inputs['Fac'])
links.new(mix_shader.outputs['Shader'], output.inputs['Surface'])
# --- 表側のマテリアル設定 ---
bsdf_front = nodes.new(type='ShaderNodeBsdfPrincipled'); bsdf_front.location = (0, 300)
links.new(bsdf_front.outputs['BSDF'], mix_shader.inputs[1])
if use_front_img:
try:
img = bpy.data.images.load(bpy.path.abspath(front_image_path))
tex = nodes.new(type='ShaderNodeTexImage'); tex.image = img; tex.location = (-200, 300)
tex.extension = front_mapping # ### 新規追加 ###: 画像の繰り返し方法を設定
# 中心を基点に変形させるためのノード群 (表側)
vec_subtract = nodes.new(type='ShaderNodeVectorMath'); vec_subtract.operation = 'SUBTRACT'; vec_subtract.location = (-800, 300)
vec_subtract.inputs[1].default_value = (0.5, 0.5, 0)
mapping = nodes.new(type='ShaderNodeMapping'); mapping.location = (-600, 300)
vec_add = nodes.new(type='ShaderNodeVectorMath'); vec_add.operation = 'ADD'; vec_add.location = (-400, 300)
vec_add.inputs[1].default_value = (0.5, 0.5, 0)
# マッピング設定 (表側)
mapping.inputs['Rotation'].default_value[2] = front_rotation
scale_val = 1.0 / front_scale if front_scale != 0 else 1.0
scale_x = scale_val * (-1 if front_flip_x else 1)
mapping.inputs['Scale'].default_value[0] = scale_x
mapping.inputs['Scale'].default_value[1] = scale_val
# ノード接続 (表側)
links.new(tex_coord.outputs['UV'], vec_subtract.inputs[0])
links.new(vec_subtract.outputs['Vector'], mapping.inputs['Vector'])
links.new(mapping.outputs['Vector'], vec_add.inputs[0])
links.new(vec_add.outputs['Vector'], tex.inputs['Vector'])
links.new(tex.outputs['Color'], bsdf_front.inputs['Base Color'])
links.new(tex.outputs['Alpha'], bsdf_front.inputs['Alpha'])
except RuntimeError:
bsdf_front.inputs['Base Color'].default_value = color
bsdf_front.inputs['Alpha'].default_value = color[3]
else:
bsdf_front.inputs['Base Color'].default_value = color
bsdf_front.inputs['Alpha'].default_value = color[3]
# --- 裏側のマテリアル設定 ---
bsdf_back = nodes.new(type='ShaderNodeBsdfPrincipled'); bsdf_back.location = (0, -300)
links.new(bsdf_back.outputs['BSDF'], mix_shader.inputs[2])
if use_back_img:
try:
img = bpy.data.images.load(bpy.path.abspath(back_image_path))
tex = nodes.new(type='ShaderNodeTexImage'); tex.image = img; tex.location = (-200, -300)
tex.extension = back_mapping # ### 新規追加 ###: 画像の繰り返し方法を設定
# 中心を基点に変形させるためのノード群 (裏側)
vec_subtract = nodes.new(type='ShaderNodeVectorMath'); vec_subtract.operation = 'SUBTRACT'; vec_subtract.location = (-800, -300)
vec_subtract.inputs[1].default_value = (0.5, 0.5, 0)
mapping = nodes.new(type='ShaderNodeMapping'); mapping.location = (-600, -300)
vec_add = nodes.new(type='ShaderNodeVectorMath'); vec_add.operation = 'ADD'; vec_add.location = (-400, -300)
vec_add.inputs[1].default_value = (0.5, 0.5, 0)
# マッピング設定 (裏側)
mapping.inputs['Rotation'].default_value[2] = back_rotation
scale_val = 1.0 / back_scale if back_scale != 0 else 1.0
scale_x = scale_val * (-1 if back_flip_x else 1)
mapping.inputs['Scale'].default_value[0] = scale_x
mapping.inputs['Scale'].default_value[1] = scale_val
# ノード接続 (裏側)
links.new(tex_coord.outputs['UV'], vec_subtract.inputs[0])
links.new(vec_subtract.outputs['Vector'], mapping.inputs['Vector'])
links.new(mapping.outputs['Vector'], vec_add.inputs[0])
links.new(vec_add.outputs['Vector'], tex.inputs['Vector'])
links.new(tex.outputs['Color'], bsdf_back.inputs['Base Color'])
links.new(tex.outputs['Alpha'], bsdf_back.inputs['Alpha'])
except RuntimeError:
bsdf_back.inputs['Base Color'].default_value = color
bsdf_back.inputs['Alpha'].default_value = color[3]
else:
bsdf_back.inputs['Base Color'].default_value = color
bsdf_back.inputs['Alpha'].default_value = color[3]
return mat
def assign_material(obj, material):
if obj.data.materials: obj.data.materials[0] = material
else: obj.data.materials.append(material)
def get_unique_sub_collection(main_coll, sub_coll_name_base):
i = 1
while True:
sub_coll_name = f"{sub_coll_name_base}_{i:03d}"
if sub_coll_name not in main_coll.children: return get_or_create_collection(sub_coll_name, main_coll)
i += 1
def create_torus_object_base(radius, thickness):
if radius < 0.0001: return None
bpy.ops.mesh.primitive_torus_add(location=(0,0,0), rotation=(0,0,0), major_radius=radius, minor_radius=thickness, major_segments=128, minor_segments=16)
return bpy.context.active_object
def join_objects(context, objects_to_join, new_name):
if not objects_to_join: return None
valid_objects = [obj for obj in objects_to_join if obj and obj.type == 'MESH']
if len(valid_objects) < 2:
if valid_objects: valid_objects[0].name = new_name; return valid_objects[0]
return None
view_layer = context.view_layer; bpy.ops.object.select_all(action='DESELECT')
for obj in valid_objects: obj.select_set(True)
view_layer.objects.active = valid_objects[0]; bpy.ops.object.join(); joined_object = view_layer.objects.active
if joined_object: joined_object.name = new_name
return joined_object
# ---------------------------------------------------------------------
# オブジェクト生成ヘルパー関数
# ---------------------------------------------------------------------
def _create_latitude_objects(props, mat):
axis=props.axis_reference; created_objects=[]; latitudes_deg=[]
if props.latitude_creation_mode == 'ANGLE':
latitudes_deg.append(0.0); angle_step = math.degrees(props.latitude_angle_step)
if angle_step < 0.01: return []
curr=angle_step
while curr<90.0-0.01: latitudes_deg.append(curr); latitudes_deg.append(-curr); curr+=angle_step
else:
n=props.latitude_divisions; num_segments=n+1; step_deg=90.0/num_segments; latitudes_deg.append(0.0)
for i in range(1, num_segments): deg=i*step_deg; latitudes_deg.append(deg); latitudes_deg.append(-deg)
base_rot=(0,0,0)
if axis=='Y': base_rot=(math.pi/2,0,0)
elif axis=='X': base_rot=(0,math.pi/2,0)
for lat_deg in latitudes_deg:
lat_rad=math.radians(lat_deg); r_ring=props.lalo_radius*math.cos(lat_rad); offset=props.lalo_radius*math.sin(lat_rad)
obj_name=f"{props.latitude_object_name}_{axis}_{int(round(lat_deg))}"
obj=create_torus_object_base(r_ring, props.latitude_thickness)
if not obj: continue
obj.name=obj_name; assign_material(obj, mat); obj.rotation_euler=base_rot
loc=mathutils.Vector((0,0,offset))
if axis=='Y': loc = mathutils.Matrix.Rotation(math.pi/2,3,'X') @ loc
elif axis=='X': loc = mathutils.Matrix.Rotation(math.pi/2,3,'Y') @ loc
obj.location=loc; created_objects.append(obj)
return created_objects
def _create_longitude_objects(props, mat):
axis=props.axis_reference; created_objects=[]; angles_rad=[]
if props.longitude_creation_mode == 'ANGLE':
step=props.longitude_angle_step; curr=0.0
while curr < 2*math.pi-1e-5: angles_rad.append(curr); curr+=step
else:
n=props.longitude_divisions; num_segments=n+1; step=(2*math.pi)/num_segments
for i in range(num_segments): angles_rad.append(i*step)
base_orientation=mathutils.Matrix.Rotation(math.pi/2,4,'X'); axis_align=mathutils.Matrix.Identity(4)
if axis=='Y': axis_align=mathutils.Matrix.Rotation(-math.pi/2,4,'X')
elif axis=='X': axis_align=mathutils.Matrix.Rotation(math.pi/2,4,'Y')
for angle in angles_rad:
deg=math.degrees(angle); obj_name=f"{props.longitude_object_name}_{axis}_{int(round(deg))%360:03d}"
obj=create_torus_object_base(props.lalo_radius, props.longitude_thickness)
if not obj: continue
obj.name=obj_name; obj.location=(0,0,0)
assign_material(obj, mat)
final_mat = axis_align @ mathutils.Matrix.Rotation(angle,4,'Z') @ base_orientation
obj.rotation_euler=final_mat.to_euler(); created_objects.append(obj)
return created_objects
def _create_highlight_surface(context):
props = context.scene.ido_keido_001_props
# 1. オブジェクトの生成と命名
bpy.ops.mesh.primitive_uv_sphere_add(segments=128, ring_count=64, radius=props.lalo_radius, location=(0, 0, 0))
obj = context.active_object
obj.visible_shadow = False
start_lon_deg = round(math.degrees(props.highlight_sphere_start_lon))
end_lon_deg = round(math.degrees(props.highlight_sphere_end_lon))
start_lat_deg = round(math.degrees(props.highlight_sphere_start_lat))
end_lat_deg = round(math.degrees(props.highlight_sphere_end_lat))
obj.name = f"{props.highlight_sphere_name}_Lon{start_lon_deg}-{end_lon_deg}_Lat{start_lat_deg}-{end_lat_deg}"
# 2. マテリアルの準備と割り当て
if props.highlight_mode == 'IMAGE':
# ### 変更 ###: 画像の繰り返し設定をマテリアル生成関数に渡す
highlight_mat = _create_highlight_image_material(
mat_name=f"{obj.name}_Material", color=props.highlight_sphere_color,
front_image_path=props.highlight_front_image,
front_scale=props.highlight_front_image_scale,
front_rotation=props.highlight_front_image_rotation,
front_flip_x=props.highlight_front_image_flip_x,
front_mapping=props.highlight_front_image_mapping,
back_image_path=props.highlight_back_image,
back_scale=props.highlight_back_image_scale,
back_rotation=props.highlight_back_image_rotation,
back_flip_x=props.highlight_back_image_flip_x,
back_mapping=props.highlight_back_image_mapping
)
else:
highlight_mat = get_or_create_material(props.highlight_sphere_color, "HighlightSurface_Color")
transparent_mat = get_or_create_material((0,0,0,0), "TransparentSurface")
obj.data.materials.append(highlight_mat)
obj.data.materials.append(transparent_mat)
# 3. UV展開 (画像テクスチャのため)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.sphere_project()
# 4. マテリアルの初期設定(一度すべてを透明に)
bpy.ops.mesh.select_all(action='SELECT')
obj.active_material_index = 1
bpy.ops.object.material_slot_assign()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
# 5. 範囲内のポリゴンを選択
start_lon = math.degrees(props.highlight_sphere_start_lon)
end_lon = math.degrees(props.highlight_sphere_end_lon)
start_lat = math.degrees(props.highlight_sphere_start_lat)
end_lat = math.degrees(props.highlight_sphere_end_lat)
mesh = obj.data
for poly in mesh.polygons:
poly_center_world = obj.matrix_world @ poly.center
lon_rad = math.atan2(poly_center_world.y, poly_center_world.x)
lon_deg = math.degrees(lon_rad)
if lon_deg < 0: lon_deg += 360
asin_arg = max(-1.0, min(1.0, poly_center_world.z / props.lalo_radius))
lat_rad = math.asin(asin_arg)
lat_deg = math.degrees(lat_rad)
is_in_lon_range = False
if start_lon <= end_lon:
if start_lon <= lon_deg <= end_lon: is_in_lon_range = True
else:
if lon_deg >= start_lon or lon_deg <= end_lon: is_in_lon_range = True
is_in_lat_range = (start_lat <= lat_deg <= end_lat)
if is_in_lon_range and is_in_lat_range:
poly.select = True
# 6. 選択したポリゴンにハイライトマテリアルを割り当て
bpy.ops.object.mode_set(mode='EDIT')
obj.active_material_index = 0
bpy.ops.object.material_slot_assign()
# 7. 後処理
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
return obj
# ---------------------------------------------------------------------
# オペレーター (変更なし)
# ---------------------------------------------------------------------
class IDO_KEIDO_001_OT_CreateTorusGuide(Operator):
bl_idname = f"{PREFIX}.create_torus_guide"; bl_label = "円環ガイドを作成"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props=context.scene.ido_keido_001_props; torus_obj=create_torus_object_base(props.torus_major_radius, props.torus_minor_radius)
if not torus_obj: return {'CANCELLED'}
plane_name=props.torus_plane; torus_obj.name=f"{props.torus_object_name}_{plane_name}"
if plane_name=='XZ': torus_obj.rotation_euler.x=math.pi/2
elif plane_name=='YZ': torus_obj.rotation_euler.y=math.pi/2
assign_material(torus_obj, get_or_create_material(props.torus_color, "TorusGuide"))
if props.use_collections:
main_coll=get_or_create_collection(props.collection_name); sub_coll=get_unique_sub_collection(main_coll, props.torus_sub_collection_name)
link_to_collection(torus_obj, sub_coll); self.report({'INFO'}, f"コレクション「{sub_coll.name}」に作成。")
else: self.report({'INFO'}, "円環ガイドを作成しました。")
return {'FINISHED'}
class IDO_KEIDO_001_OT_CreateTorusGuideAllPlanes(Operator):
bl_idname = f"{PREFIX}.create_torus_guide_all_planes"; bl_label = "3平面に円環ガイドを作成"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props, sub_coll = context.scene.ido_keido_001_props, None
if props.use_collections: main_coll=get_or_create_collection(props.collection_name); sub_coll=get_unique_sub_collection(main_coll, f"{props.torus_sub_collection_name}_3Planes")
planes_rot={'XY': (0,0,0), 'XZ':(math.pi/2,0,0), 'YZ':(0,math.pi/2,0)}; mat=get_or_create_material(props.torus_color, "TorusGuide"); created_objects=[]
for name, rotation in planes_rot.items():
torus_obj=create_torus_object_base(props.torus_major_radius, props.torus_minor_radius)
if not torus_obj: continue
torus_obj.name=f"{props.torus_object_name}_{name}"; torus_obj.rotation_euler=rotation; assign_material(torus_obj, mat); created_objects.append(torus_obj)
if props.join_torus_guides and created_objects:
joined_name = f"{props.torus_object_name}_Joined_3Planes"; joined_obj = join_objects(context, created_objects, joined_name)
if sub_coll and joined_obj: link_to_collection(joined_obj, sub_coll)
self.report({'INFO'}, f"3つの円環ガイドを一体化し「{sub_coll.name}」に作成。")
elif created_objects:
if sub_coll:
for obj in created_objects: link_to_collection(obj, sub_coll)
self.report({'INFO'}, f"3つの円環ガイドを「{sub_coll.name}」に作成。")
else: self.report({'INFO'}, "3つの円環ガイドを作成しました。")
return {'FINISHED'}
class IDO_KEIDO_001_OT_CreateLatitudeLines(Operator):
bl_idname = f"{PREFIX}.create_latitude_lines"; bl_label = "緯線を作成"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props=context.scene.ido_keido_001_props; mat=get_or_create_material(props.latitude_color, "LatitudeColor")
created_objects = _create_latitude_objects(props, mat)
if not created_objects: return {'CANCELLED'}
if props.join_latitudes:
joined_name = f"{props.latitude_object_name}_{props.axis_reference}_Joined"
joined_obj = join_objects(context, created_objects, joined_name)
if props.use_collections and joined_obj:
main_coll = get_or_create_collection(props.collection_name); sub_coll_name = f"{props.latitude_sub_collection_name}_{props.axis_reference}_Joined"
sub_coll = get_unique_sub_collection(main_coll, sub_coll_name); link_to_collection(joined_obj, sub_coll)
self.report({'INFO'}, f"緯線を一体化し「{sub_coll.name}」に作成。")
else: self.report({'INFO'}, "緯線を一体化して作成しました。")
else:
if props.use_collections:
main_coll=get_or_create_collection(props.collection_name); sub_coll=get_unique_sub_collection(main_coll, f"{props.latitude_sub_collection_name}_{props.axis_reference}")
for obj in created_objects: link_to_collection(obj, sub_coll)
self.report({'INFO'}, f"緯線を「{sub_coll.name}」に作成。")
else: self.report({'INFO'}, f"{len(created_objects)}本の緯線を作成しました。")
return {'FINISHED'}
class IDO_KEIDO_001_OT_CreateLongitudeLines(Operator):
bl_idname = f"{PREFIX}.create_longitude_lines"; bl_label = "経線を作成"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props=context.scene.ido_keido_001_props; mat=get_or_create_material(props.longitude_color, "LongitudeColor")
created_objects = _create_longitude_objects(props, mat)
if not created_objects: return {'CANCELLED'}
if props.join_longitudes:
joined_name = f"{props.longitude_object_name}_{props.axis_reference}_Joined"
joined_obj = join_objects(context, created_objects, joined_name)
if props.use_collections and joined_obj:
main_coll = get_or_create_collection(props.collection_name); sub_coll_name = f"{props.longitude_sub_collection_name}_{props.axis_reference}_Joined"
sub_coll = get_unique_sub_collection(main_coll, sub_coll_name); link_to_collection(joined_obj, sub_coll)
self.report({'INFO'}, f"経線を一体化し「{sub_coll.name}」に作成。")
else: self.report({'INFO'}, "経線を一体化して作成しました。")
else:
if props.use_collections:
main_coll=get_or_create_collection(props.collection_name); sub_coll=get_unique_sub_collection(main_coll, f"{props.longitude_sub_collection_name}_{props.axis_reference}")
for obj in created_objects: link_to_collection(obj, sub_coll)
self.report({'INFO'}, f"経線を「{sub_coll.name}」に作成。")
else: self.report({'INFO'}, f"{len(created_objects)}本の経線を作成しました。")
return {'FINISHED'}
class IDO_KEIDO_001_OT_CreateHighlightSurface(Operator):
bl_idname = f"{PREFIX}.create_highlight_surface"
bl_label = "球体表面ハイライトを作成"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props = context.scene.ido_keido_001_props
surface_obj = _create_highlight_surface(context)
if props.use_collections:
main_coll = get_or_create_collection(props.collection_name)
sub_coll = get_unique_sub_collection(main_coll, props.highlight_sub_collection_name)
link_to_collection(surface_obj, sub_coll)
self.report({'INFO'}, f"ハイライト用表面を「{sub_coll.name}」に作成しました。")
else:
self.report({'INFO'}, "ハイライト用表面を作成しました。")
return {'FINISHED'}
class IDO_KEIDO_001_OT_CreateAllLatLonLines(Operator):
bl_idname = f"{PREFIX}.create_all_latlon_lines"; bl_label = "緯線と経線を作成"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props=context.scene.ido_keido_001_props
lat_mat=get_or_create_material(props.latitude_color, "LatitudeColor"); lon_mat=get_or_create_material(props.longitude_color, "LongitudeColor")
lat_objects=_create_latitude_objects(props,lat_mat); lon_objects=_create_longitude_objects(props,lon_mat)
if not lat_objects and not lon_objects: return {'CANCELLED'}
processed_lats=[]; processed_lons=[]
if props.join_latitudes and lat_objects:
obj=join_objects(context,lat_objects,f"{props.latitude_object_name}_{props.axis_reference}_Joined");
if obj: processed_lats.append(obj)
else: processed_lats.extend(lat_objects)
if props.join_longitudes and lon_objects:
obj=join_objects(context,lon_objects,f"{props.longitude_object_name}_{props.axis_reference}_Joined");
if obj: processed_lons.append(obj)
else: processed_lons.extend(lon_objects)
if props.join_latlon_together:
all_objs=processed_lats+processed_lons
if all_objs:
final_obj=join_objects(context,all_objs,f"LatLon_Sphere_{props.axis_reference}_Joined")
if props.use_collections and final_obj:
main_coll=get_or_create_collection(props.collection_name); sub_coll=get_unique_sub_collection(main_coll,f"LatLon_Sphere_{props.axis_reference}_Joined")
link_to_collection(final_obj,sub_coll); self.report({'INFO'},f"緯線・経線を一体化し「{sub_coll.name}」に作成。")
else: self.report({'INFO'},"緯線・経線を一体化して作成しました。")
else:
if props.use_collections:
main_coll=get_or_create_collection(props.collection_name)
if processed_lats:
lat_sub_coll_name=f"{props.latitude_sub_collection_name}_{props.axis_reference}";
if props.join_latitudes: lat_sub_coll_name+="_Joined"
lat_sub_coll=get_unique_sub_collection(main_coll,lat_sub_coll_name)
for obj in processed_lats: link_to_collection(obj,lat_sub_coll)
if processed_lons:
lon_sub_coll_name=f"{props.longitude_sub_collection_name}_{props.axis_reference}";
if props.join_longitudes: lon_sub_coll_name+="_Joined"
lon_sub_coll=get_unique_sub_collection(main_coll,lon_sub_coll_name)
for obj in processed_lons: link_to_collection(obj,lon_sub_coll)
self.report({'INFO'},"緯線と経線をそれぞれのコレクションに作成。")
else: self.report({'INFO'},"緯線と経線を作成しました。")
return {'FINISHED'}
class IDO_KEIDO_001_OT_ResetTorusGuideSettings(Operator):
bl_idname = f"{PREFIX}.reset_torus_guide_settings"; bl_label = "円環ガイド設定をリセット"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props=context.scene.ido_keido_001_props; defaults=addon_defaults
props.torus_plane=defaults["torus_plane"]; props.torus_major_radius=defaults["torus_major_radius"]
props.torus_minor_radius=defaults["torus_minor_radius"]; props.torus_color=defaults["torus_color"]
props.torus_sub_collection_name=defaults["torus_sub_collection_name"]; props.torus_object_name=defaults["torus_object_name"]
props.join_torus_guides = defaults["join_torus_guides"]; self.report({'INFO'}, "円環ガイドの設定を初期値にリセットしました。"); return {'FINISHED'}
class IDO_KEIDO_001_OT_ResetLatLonSettings(Operator):
bl_idname = f"{PREFIX}.reset_latlon_settings"; bl_label = "緯線・経線設定をリセット"; bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
props=context.scene.ido_keido_001_props; defaults=addon_defaults
props.axis_reference=defaults["axis_reference"]; props.lalo_radius=defaults["lalo_radius"]
props.latitude_thickness=defaults["latitude_thickness"]
props.longitude_thickness=defaults["longitude_thickness"]
props.latitude_color=defaults["latitude_color"]; props.longitude_color=defaults["longitude_color"]
props.latitude_creation_mode=defaults["latitude_creation_mode"]; props.latitude_angle_step=defaults["latitude_angle_step"]
props.latitude_divisions=defaults["latitude_divisions"]; props.latitude_object_name=defaults["latitude_object_name"]
props.longitude_creation_mode=defaults["longitude_creation_mode"]; props.longitude_angle_step=defaults["longitude_angle_step"]
props.longitude_divisions=defaults["longitude_divisions"]; props.longitude_object_name=defaults["longitude_object_name"]
props.join_latitudes = defaults["join_latitudes"]; props.join_longitudes = defaults["join_longitudes"]
props.join_latlon_together = defaults["join_latlon_together"]
props.highlight_sphere_color = defaults["highlight_sphere_color"]
props.highlight_sphere_name = defaults["highlight_sphere_name"]
props.highlight_sphere_start_lon = defaults["highlight_sphere_start_lon"]
props.highlight_sphere_end_lon = defaults["highlight_sphere_end_lon"]
props.highlight_sphere_start_lat = defaults["highlight_sphere_start_lat"]
props.highlight_sphere_end_lat = defaults["highlight_sphere_end_lat"]
props.highlight_mode = defaults["highlight_mode"]
props.highlight_front_image = defaults["highlight_front_image"]
props.highlight_back_image = defaults["highlight_back_image"]
# ### 追加 ###: 画像変形プロパティをリセット
props.highlight_front_image_scale = defaults["highlight_front_image_scale"]
props.highlight_front_image_rotation = defaults["highlight_front_image_rotation"]
props.highlight_front_image_flip_x = defaults["highlight_front_image_flip_x"]
props.highlight_back_image_scale = defaults["highlight_back_image_scale"]
props.highlight_back_image_rotation = defaults["highlight_back_image_rotation"]
props.highlight_back_image_flip_x = defaults["highlight_back_image_flip_x"]
# ### 新規追加 ###: 画像マッピングプロパティをリセット
props.highlight_front_image_mapping = defaults["highlight_front_image_mapping"]
props.highlight_back_image_mapping = defaults["highlight_back_image_mapping"]
self.report({'INFO'}, "緯線・経線の設定を初期値にリセットしました。"); return {'FINISHED'}
class IDO_KEIDO_001_OT_OpenURL(Operator):
bl_idname = f"{PREFIX}.open_url"; bl_label = "Open URL"; url: StringProperty(default="");
def execute(self, context): webbrowser.open(self.url); return {'FINISHED'}
def delayed_unregister(): unregister(); return None
class IDO_KEIDO_001_OT_RemoveAddon(Operator):
bl_idname = f"{PREFIX}.remove_addon"; bl_label = "アドオンを無効化してUIを削除"; bl_options = {'REGISTER'}
@classmethod
def poll(cls, context): return not bpy.app.timers.is_registered(delayed_unregister)
def execute(self, context):
bpy.ops.preferences.addon_disable(module=__name__); bpy.app.timers.register(delayed_unregister)
self.report({'INFO'}, f"アドオン '{bl_info['name']}' を無効化しました。UIを削除します。"); return {'FINISHED'}
# ---------------------------------------------------------------------
# UI パネル
# ---------------------------------------------------------------------
class IDO_KEIDO_001_PT_LatLonPanel(Panel):
bl_label = "2. 緯線・経線 作成"; bl_idname = f"{PREFIX}_PT_latlon_panel"; bl_space_type='VIEW_3D'; bl_region_type='UI'; bl_category=ADDON_CATEGORY_NAME
def draw(self, context):
layout, props = self.layout, context.scene.ido_keido_001_props
row = layout.row(align=True); row.alignment = 'RIGHT'; row.operator(IDO_KEIDO_001_OT_ResetLatLonSettings.bl_idname, text="リセット", icon='FILE_REFRESH')
box_axis=layout.box(); row=box_axis.row(); row.label(text="極となる基準軸:", icon='EMPTY_AXIS'); row.prop(props, "axis_reference", text="")
box_common=layout.box(); box_common.label(text="共通球体設定"); col=box_common.column(align=True); col.prop(props, "lalo_radius")
row=layout.row(); col1=row.column(); box1=col1.box(); box1.label(text="緯線 (Parallels)", icon='MESH_GRID'); cc=box1.column(align=True)
cc.prop(props, "latitude_thickness")
cc.prop(props,"latitude_color",text=""); cc.prop(props,"latitude_creation_mode",text="");
if props.latitude_creation_mode=='ANGLE': cc.prop(props, "latitude_angle_step")
else: cc.prop(props, "latitude_divisions")
cc.prop(props, "join_latitudes", text="一体化")
col2=row.column(); box2=col2.box(); box2.label(text="経線 (Meridians)", icon='MESH_UVSPHERE'); cc=box2.column(align=True)
cc.prop(props, "longitude_thickness")
cc.prop(props,"longitude_color",text=""); cc.prop(props,"longitude_creation_mode",text="")
if props.longitude_creation_mode=='ANGLE': cc.prop(props, "longitude_angle_step")
else: cc.prop(props, "longitude_divisions")
cc.prop(props, "join_longitudes", text="一体化")
box_name=layout.box(); box_name.label(text="命名とコレクション"); col=box_name.column(align=True)
col.prop(props,"latitude_object_name",text="緯線名Prefix"); col.prop(props,"longitude_object_name",text="経線名Prefix")
layout.separator(); axis_str=f"({props.axis_reference}軸基準)"; row=layout.row(align=True); row.scale_y=1.2
row.operator(IDO_KEIDO_001_OT_CreateLatitudeLines.bl_idname,text=f"緯線を作成 {axis_str}",icon='MESH_GRID'); row.operator(IDO_KEIDO_001_OT_CreateLongitudeLines.bl_idname,text=f"経線を作成 {axis_str}",icon='MESH_UVSPHERE')
box_join_all=layout.box(); box_join_all.prop(props,"join_latlon_together",text="緯線と経線を最終的に結合")
row2=layout.row(); row2.scale_y=1.5; row2.operator(IDO_KEIDO_001_OT_CreateAllLatLonLines.bl_idname,text=f"緯線と経線を両方作成 {axis_str}",icon='WORLD')
layout.separator(factor=2)
box_highlight = layout.box()
box_highlight.label(text="3. 球体表面ハイライト", icon='MATERIAL')
col_highlight = box_highlight.column(align=True)
col_highlight.prop(props, "highlight_mode", expand=True)
# ### 変更 ###: 画像の繰り返し設定UIを追加
if props.highlight_mode == 'IMAGE':
img_box = col_highlight.box()
img_box.prop(props, "highlight_front_image", text="オモテ画像")
row_front_map = img_box.row()
row_front_map.prop(props, "highlight_front_image_mapping", expand=True)
row_front_transform = img_box.row(align=True)
row_front_transform.prop(props, "highlight_front_image_scale")
row_front_transform.prop(props, "highlight_front_image_rotation")
row_front_transform.prop(props, "highlight_front_image_flip_x")
img_box.separator()
img_box.prop(props, "highlight_back_image", text="ウラ画像")
row_back_map = img_box.row()
row_back_map.prop(props, "highlight_back_image_mapping", expand=True)
row_back_transform = img_box.row(align=True)
row_back_transform.prop(props, "highlight_back_image_scale")
row_back_transform.prop(props, "highlight_back_image_rotation")
row_back_transform.prop(props, "highlight_back_image_flip_x")
col_highlight.prop(props, "highlight_sphere_color", text="色 (Fallback)")
row_lon_range = col_highlight.row(align=True)
row_lon_range.prop(props, "highlight_sphere_start_lon")
row_lon_range.prop(props, "highlight_sphere_end_lon")
row_lat_range = col_highlight.row(align=True)
row_lat_range.prop(props, "highlight_sphere_start_lat")
row_lat_range.prop(props, "highlight_sphere_end_lat")
col_highlight.prop(props, "highlight_sphere_name", text="オブジェクト名")
col_highlight.prop(props, "highlight_sub_collection_name", text="コレクション名")
row_highlight = box_highlight.row()
row_highlight.scale_y = 1.5
row_highlight.operator(IDO_KEIDO_001_OT_CreateHighlightSurface.bl_idname, text="球体ハイライトを作成", icon='ADD')
class IDO_KEIDO_001_PT_LinksPanel(Panel):
bl_label="8. リンク集"; bl_idname=f"{PREFIX}_PT_links"; bl_space_type='VIEW_3D'; bl_region_type='UI'; bl_category=ADDON_CATEGORY_NAME; bl_options={'DEFAULT_CLOSED'}
def draw(self, context):
layout=self.layout
def draw_links_in_box(link_list, title):
box=layout.box(); box.label(text=title)
col=box.column(align=True)
for link in link_list:
op=col.operator(IDO_KEIDO_001_OT_OpenURL.bl_idname, text=link["label"], icon='URL'); op.url = link["url"]
draw_links_in_box(THIS_LINKS, "このアドオンのドキュメント:")
draw_links_in_box(ADDON_LINKS, "関連ドキュメント:")
draw_links_in_box(NEW_DOC_LINKS, "更新情報 / 目次:")
draw_links_in_box(DOC_LINKS, "過去のドキュメント:")
draw_links_in_box(SOCIAL_LINKS, "関連リンク / SNS:")
class IDO_KEIDO_001_PT_AdminPanel(Panel):
bl_label="9. 設定"; bl_idname=f"{PREFIX}_PT_admin_panel"; bl_space_type='VIEW_3D'; bl_region_type='UI'; bl_category=ADDON_CATEGORY_NAME; bl_options={'DEFAULT_CLOSED'}
def draw(self, context):
layout,props=self.layout,context.scene.ido_keido_001_props; box=layout.box(); box.label(text="コレクション設定",icon='OUTLINER_COLLECTION')
box.prop(props,"use_collections",text="コレクションを作成して整理"); row=box.row(); row.enabled=props.use_collections
row.prop(props,"collection_name",text="ルートコレクション名"); row=box.row(); row.enabled=props.use_collections
row.prop(props,"latitude_sub_collection_name",text="緯線サブフォルダ"); row.prop(props,"longitude_sub_collection_name",text="経線サブフォルダ")
layout.separator(); layout.operator(IDO_KEIDO_001_OT_RemoveAddon.bl_idname, text="このアドオンを無効化", icon='CANCEL')
layout.label(text=f"Version: {'.'.join(map(str, bl_info['version']))}")
# ---------------------------------------------------------------------
# プロパティ定義
# ---------------------------------------------------------------------
class IdoKeido001Properties(PropertyGroup):
torus_plane:EnumProperty(name="配置平面",items=[('XZ',"XZ", ""),('XY',"XY",""),('YZ',"YZ","")],default=addon_defaults["torus_plane"])
torus_major_radius:FloatProperty(name="半径",default=addon_defaults["torus_major_radius"],min=0.01,unit='LENGTH')
torus_minor_radius:FloatProperty(name="太さ",default=addon_defaults["torus_minor_radius"],min=0.001,unit='LENGTH',precision=3)
torus_color:FloatVectorProperty(name="色",subtype='COLOR',size=4,default=addon_defaults["torus_color"],min=0.0,max=1.0)
torus_sub_collection_name:StringProperty(name="ガイドCol名",default=addon_defaults["torus_sub_collection_name"])
torus_object_name:StringProperty(name="ガイドObj名",default=addon_defaults["torus_object_name"])
axis_reference:EnumProperty(name="基準軸 (極)",items=[('Z',"Z軸", ""),('Y',"Y軸",""),('X',"X軸","")],default=addon_defaults["axis_reference"],description="この軸が球体の極となります")
lalo_radius:FloatProperty(name="球体半径",default=addon_defaults["lalo_radius"],min=0.01,unit='LENGTH')
latitude_thickness:FloatProperty(name="緯線の太さ",default=addon_defaults["latitude_thickness"],min=0.001,unit='LENGTH',precision=3)
longitude_thickness:FloatProperty(name="経線の太さ",default=addon_defaults["longitude_thickness"],min=0.001,unit='LENGTH',precision=3)
latitude_color:FloatVectorProperty(name="緯線の色",subtype='COLOR',size=4,default=addon_defaults["latitude_color"],min=0.0,max=1.0)
longitude_color:FloatVectorProperty(name="経線の色",subtype='COLOR',size=4,default=addon_defaults["longitude_color"],min=0.0,max=1.0)
latitude_creation_mode:EnumProperty(name="緯線生成",items=[('ANGLE',"角度指定",""),('DIVISIONS',"分割数指定","")],default=addon_defaults["latitude_creation_mode"])
latitude_angle_step:FloatProperty(name="Step",subtype='ANGLE',default=addon_defaults["latitude_angle_step"],min=math.radians(1.0),max=math.radians(89.0))
latitude_divisions:IntProperty(name="N (赤道~極)",description="赤道から極までをN+1分割",default=addon_defaults["latitude_divisions"],min=1,max=90)
latitude_object_name:StringProperty(name="Lat Obj Name",default=addon_defaults["latitude_object_name"])
longitude_creation_mode:EnumProperty(name="経線生成",items=[('ANGLE',"角度指定",""),('DIVISIONS',"分割数指定","")],default=addon_defaults["longitude_creation_mode"])
longitude_angle_step:FloatProperty(name="Step",subtype='ANGLE',default=addon_defaults["longitude_angle_step"],min=math.radians(1.0),max=math.radians(180.0))
longitude_divisions:IntProperty(name="N (一周)",description="360度をN+1分割",default=addon_defaults["longitude_divisions"],min=1,max=360)
longitude_object_name:StringProperty(name="Lon Obj Name",default=addon_defaults["longitude_object_name"])
use_collections:BoolProperty(name="Use Collections",default=addon_defaults["use_collections"])
collection_name:StringProperty(name="Root Collection",default=addon_defaults["collection_name"])
latitude_sub_collection_name:StringProperty(name="Lat Col Name",default=addon_defaults["latitude_sub_collection_name"])
longitude_sub_collection_name:StringProperty(name="Lon Col Name",default=addon_defaults["longitude_sub_collection_name"])
join_torus_guides: BoolProperty(name="一体化", description="生成した3平面のガイドを1つのオブジェクトに結合します", default=addon_defaults["join_torus_guides"])
join_latitudes: BoolProperty(name="一体化", description="生成した緯線を1つのオブジェクトに結合します", default=addon_defaults["join_latitudes"])
join_longitudes: BoolProperty(name="一体化", description="生成した経線を1つのオブジェクトに結合します", default=addon_defaults["join_longitudes"])
join_latlon_together: BoolProperty(name="緯線と経線を結合", description="生成した緯線と経線をすべて1つのオブジェクトに結合します", default=addon_defaults["join_latlon_together"])
highlight_sphere_color:FloatVectorProperty(name="色",subtype='COLOR',size=4,default=addon_defaults["highlight_sphere_color"],min=0.0,max=1.0)
highlight_sphere_name:StringProperty(name="名前",default=addon_defaults["highlight_sphere_name"])
highlight_sub_collection_name:StringProperty(name="コレクション名",default=addon_defaults["highlight_sub_collection_name"])
highlight_sphere_start_lon:FloatProperty(name="経度 開始",description="ハイライトを開始する経度 (0-360度, +X軸が0度)",default=addon_defaults["highlight_sphere_start_lon"],min=0.0,max=math.radians(360.0),unit='ROTATION',subtype='ANGLE',precision=1)
highlight_sphere_end_lon:FloatProperty(name="経度 終了",description="ハイライトを終了する経度 (0-360度, +X軸が0度)",default=addon_defaults["highlight_sphere_end_lon"],min=0.0,max=math.radians(360.0),unit='ROTATION',subtype='ANGLE',precision=1)
highlight_sphere_start_lat:FloatProperty(name="緯度 開始",description="ハイライトを開始する緯度 (-90 - +90度)",default=addon_defaults["highlight_sphere_start_lat"],min=math.radians(-90.0),max=math.radians(90.0),unit='ROTATION',subtype='ANGLE',precision=1)
highlight_sphere_end_lat:FloatProperty(name="緯度 終了",description="ハイライトを終了する緯度 (-90 - +90度)",default=addon_defaults["highlight_sphere_end_lat"],min=math.radians(-90.0),max=math.radians(90.0),unit='ROTATION',subtype='ANGLE',precision=1)
highlight_mode: EnumProperty(
name="モード",
items=[('COLOR', "色のみ", "指定した色でハイライトします"),
('IMAGE', "画像テクスチャ", "表裏に別々の画像を貼り付けます")],
default=addon_defaults["highlight_mode"]
)
highlight_front_image: StringProperty(name="オモテ画像",description="ハイライト表面に貼る画像 (表側)",default=addon_defaults["highlight_front_image"],subtype='FILE_PATH')
highlight_back_image: StringProperty(name="ウラ画像",description="ハイライト表面に貼る画像 (裏側)",default=addon_defaults["highlight_back_image"],subtype='FILE_PATH')
# ### 追加 ###: 画像変形プロパティ
highlight_front_image_scale: FloatProperty(name="縮小割合", description="画像の縮小・拡大率。2を指定すると1/2のサイズになります", default=addon_defaults["highlight_front_image_scale"], min=0.01, soft_max=10.0, precision=2)
highlight_front_image_rotation: FloatProperty(name="回転", description="画像をZ軸中心に回転します", subtype='ANGLE', default=addon_defaults["highlight_front_image_rotation"], unit='ROTATION')
highlight_front_image_flip_x: BoolProperty(name="左右反転", description="画像を左右に反転します", default=addon_defaults["highlight_front_image_flip_x"])
highlight_back_image_scale: FloatProperty(name="縮小割合", description="画像の縮小・拡大率。2を指定すると1/2のサイズになります", default=addon_defaults["highlight_back_image_scale"], min=0.01, soft_max=10.0, precision=2)
highlight_back_image_rotation: FloatProperty(name="回転", description="画像をZ軸中心に回転します", subtype='ANGLE', default=addon_defaults["highlight_back_image_rotation"], unit='ROTATION')
highlight_back_image_flip_x: BoolProperty(name="左右反転", description="画像を左右に反転します", default=addon_defaults["highlight_back_image_flip_x"])
# ### 新規追加 ###: 画像の繰り返し設定プロパティ
highlight_front_image_mapping: EnumProperty(
name="貼り方",
items=[('CLIP', "1つだけ貼る", "画像を縮小して中央に1つだけ配置します"),
('REPEAT', "周辺に繰り返しで貼る", "画像をタイル状に繰り返し配置します")],
default=addon_defaults["highlight_front_image_mapping"]
)
highlight_back_image_mapping: EnumProperty(
name="貼り方",
items=[('CLIP', "1つだけ貼る", "画像を縮小して中央に1つだけ配置します"),
('REPEAT', "周辺に繰り返しで貼る", "画像をタイル状に繰り返し配置します")],
default=addon_defaults["highlight_back_image_mapping"]
)
# ---------------------------------------------------------------------
# 登録クラスリスト (変更なし)
# ---------------------------------------------------------------------
classes = (
IdoKeido001Properties, IDO_KEIDO_001_OT_CreateTorusGuide, IDO_KEIDO_001_OT_CreateTorusGuideAllPlanes,
IDO_KEIDO_001_OT_CreateLatitudeLines, IDO_KEIDO_001_OT_CreateLongitudeLines,
IDO_KEIDO_001_OT_CreateHighlightSurface,
IDO_KEIDO_001_OT_CreateAllLatLonLines,
IDO_KEIDO_001_OT_ResetTorusGuideSettings, IDO_KEIDO_001_OT_ResetLatLonSettings, IDO_KEIDO_001_OT_OpenURL, IDO_KEIDO_001_OT_RemoveAddon,
IDO_KEIDO_001_PT_LatLonPanel, IDO_KEIDO_001_PT_LinksPanel, IDO_KEIDO_001_PT_AdminPanel,
)
# ---------------------------------------------------------------------
# 登録・登録解除処理 (変更なし)
# ---------------------------------------------------------------------
def register():
for cls in classes: bpy.utils.register_class(cls)
bpy.types.Scene.ido_keido_001_props = PointerProperty(type=IdoKeido001Properties)
def unregister():
for cls in reversed(classes):
if hasattr(bpy.utils, 'unregister_class'):
try: bpy.utils.unregister_class(cls)
except RuntimeError: pass
if hasattr(bpy.types.Scene, 'ido_keido_001_props'): del bpy.types.Scene.ido_keido_001_props
if __name__ == "__main__":
register()