import bpy
import bmesh
import math
import random
import os
import re
import time
from mathutils import Vector, Euler
from bpy.props import (
    FloatProperty, IntProperty, FloatVectorProperty, 
    EnumProperty, PointerProperty, StringProperty, BoolProperty
)
from bpy.types import Operator, Panel, PropertyGroup
from bpy.app.handlers import persistent

# --- DFLT_VALS_START ---
dflt_vls_dct = {
    "dm_rd_vl": 30.0,
    "pt_rd_vl": 1.5,
    "lt_cn_vl": 6,
    "lt_th_vl": 0.20000000298023224,
    "lt_sg_vl": 32,
    "ln_cn_vl": 12,
    "ln_th_vl": 0.20000000298023224,
    "ln_sg_vl": 16,
    "dm_cl_vl": (0.5676, 0.3865, 0.8000, 0.3000),
    "fc_cl_vl": (1.0000, 0.8000, 0.2000, 0.7000),
    "pt_md_st": "RANDOM",
    "rn_sd_vl": 333,
    "p1_u_vl": 30.0,
    "p1_v_vl": 30.0,
    "p2_u_vl": 150.0,
    "p2_v_vl": 60.0,
    "p3_u_vl": 270.0,
    "p3_v_vl": 45.0,
    "sq_sz_vl": 30.0,
    "tp_cl_vl": (0.2000, 0.8000, 0.8000, 0.5000),
    
    # TCS (Torus, Cone, Sphere) default values
    "tcs_loc_vl": (0.0, 0.0, 0.0),
    "tcs_rot_vl": (0.0, 0.0, 0.0),
    "tr_mj_rd": 2.0,
    "tr_mn_rd": 0.2,
    "cn_ht_vl": 3.0,
    "sp_rd_vl": 0.5,
    "tr_cl_vl": (0.2, 0.6, 1.0, 1.0),
    "cn_cl_vl": (1.0, 0.5, 0.1, 1.0),
    "sp_cl_vl": (0.2, 0.9, 0.4, 1.0),
    "tr_shw_fl": True,
    "cn_shw_fl": True,
    "sp_shw_fl": True,
}
# --- DFLT_VALS_END ---

bl_info = {
    "name": "zionad 1000[ Hemisphere Dome & TCS ]",
    "author": "zionadchat",
    "version": (1, 3, 0),
    "blender": (5, 0, 0),
    "category": "   1000[   Hemisphere Dome  ]   ",
    "description": "Smoothness control, Geometric Info & TCS Generator (Blender 5+)",
    "location": "View3D > Sidebar",
}

prfx_str = "hm_dm_1000"
clc_nm_str = f"{prfx_str}_clc"
mt_dm_nm_str = f"{prfx_str}_mt_dm"
mt_fc_nm_str = f"{prfx_str}_mt_fc"
mt_pt_nm_str = f"{prfx_str}_mt_pt"
mt_tp_nm_str = f"{prfx_str}_mt_tp"

def gt_or_cr_clc(nm_str):
    clc_dt = bpy.data.collections.get(nm_str)
    if not clc_dt:
        clc_dt = bpy.data.collections.new(nm_str)
        bpy.context.scene.collection.children.link(clc_dt)
    return clc_dt

def gt_or_cr_obj(nm_str, typ_str='MESH', dt_nm_str=None):
    clc_dt = gt_or_cr_clc(clc_nm_str)
    if dt_nm_str is None:
        dt_nm_str = nm_str + "_dt"
    
    obj_dt = bpy.data.objects.get(nm_str)
    if not obj_dt:
        if typ_str == 'MESH':
            m_dt = bpy.data.meshes.get(dt_nm_str) or bpy.data.meshes.new(dt_nm_str)
        elif typ_str == 'CURVE':
            m_dt = bpy.data.curves.get(dt_nm_str) or bpy.data.curves.new(dt_nm_str, type='CURVE')
            m_dt.dimensions = '3D'
            m_dt.fill_mode = 'FULL'
        obj_dt = bpy.data.objects.new(nm_str, m_dt)
        clc_dt.objects.link(obj_dt)
    else:
        if obj_dt.name not in clc_dt.objects:
            for c_dt in obj_dt.users_collection:
                c_dt.objects.unlink(obj_dt)
            clc_dt.objects.link(obj_dt)
    return obj_dt

def gt_or_cr_mt(nm_str):
    mt_dt = bpy.data.materials.get(nm_str)
    if not mt_dt:
        mt_dt = bpy.data.materials.new(nm_str)
        mt_dt.use_nodes = True
        if hasattr(mt_dt, 'blend_method'):
            mt_dt.blend_method = 'BLEND'
        if hasattr(mt_dt, 'shadow_method'):
            mt_dt.shadow_method = 'NONE'
    return mt_dt

def st_mt_cl(mt_dt, cl_vl):
    if not mt_dt.use_nodes:
        mt_dt.use_nodes = True
    nd_dt = mt_dt.node_tree.nodes
    pr_nd = nd_dt.get("Principled BSDF")
    if pr_nd:
        pr_nd.inputs["Base Color"].default_value = (cl_vl[0], cl_vl[1], cl_vl[2], 1.0)
        pr_nd.inputs["Alpha"].default_value = cl_vl[3] if len(cl_vl) == 4 else 1.0

def fmt_eq(a, b, c, d):
    sgn_b = '+' if b >= 0 else '-'
    sgn_c = '+' if c >= 0 else '-'
    sgn_d = '+' if d >= 0 else '-'
    return f"{a:.1f}x {sgn_b} {abs(b):.1f}y {sgn_c} {abs(c):.1f}z {sgn_d} {abs(d):.1f} = 0"

def rbld_dm_fn():
    ctx_env = bpy.context
    if not ctx_env or not hasattr(ctx_env, "scene"): return None
    prp_dt = ctx_env.scene.hm_dm_prps
    rd_vl = prp_dt.dm_rd_vl
    
    ln_obj = gt_or_cr_obj(f"{prfx_str}_dm_ln", 'CURVE')
    ln_crv = ln_obj.data
    ln_crv.splines.clear()
    
    ln_cn = prp_dt.ln_cn_vl
    ln_sg = prp_dt.ln_sg_vl
    for i_idx in range(ln_cn):
        th_vl = (2 * math.pi / ln_cn) * i_idx
        spl_dt = ln_crv.splines.new('POLY')
        spl_dt.points.add(ln_sg - 1)
        for j_idx in range(ln_sg):
            ph_vl = (math.pi / 2) * (j_idx / (ln_sg - 1))
            x_vl = rd_vl * math.sin(ph_vl) * math.cos(th_vl)
            y_vl = rd_vl * math.sin(ph_vl) * math.sin(th_vl)
            z_vl = rd_vl * math.cos(ph_vl)
            spl_dt.points[j_idx].co = (x_vl, y_vl, z_vl, 1.0)
    ln_crv.bevel_depth = prp_dt.ln_th_vl
    ln_crv.bevel_resolution = 4
    
    lt_obj = gt_or_cr_obj(f"{prfx_str}_dm_lt", 'CURVE')
    lt_crv = lt_obj.data
    lt_crv.splines.clear()
    
    lt_cn = prp_dt.lt_cn_vl
    lt_sg = prp_dt.lt_sg_vl
    for i_idx in range(1, lt_cn + 1):
        ph_vl = (math.pi / 2) * (i_idx / lt_cn)
        spl_dt = lt_crv.splines.new('POLY')
        spl_dt.points.add(lt_sg - 1)
        for j_idx in range(lt_sg):
            th_vl = (2 * math.pi) * (j_idx / (lt_sg - 1))
            x_vl = rd_vl * math.sin(ph_vl) * math.cos(th_vl)
            y_vl = rd_vl * math.sin(ph_vl) * math.sin(th_vl)
            z_vl = rd_vl * math.cos(ph_vl)
            spl_dt.points[j_idx].co = (x_vl, y_vl, z_vl, 1.0)
        spl_dt.use_cyclic_u = True
    lt_crv.bevel_depth = prp_dt.lt_th_vl
    lt_crv.bevel_resolution = 4

    mt_dm = gt_or_cr_mt(mt_dm_nm_str)
    st_mt_cl(mt_dm, prp_dt.dm_cl_vl)
    
    for ob_dt in [ln_obj, lt_obj]:
        if not ob_dt.data.materials: ob_dt.data.materials.append(mt_dm)
        else: ob_dt.data.materials[0] = mt_dm
    
    if ctx_env.view_layer: ctx_env.view_layer.update()
    return None

def rbld_pt_fc_fn():
    ctx_env = bpy.context
    if not ctx_env or not hasattr(ctx_env, "scene"): return None
    prp_dt = ctx_env.scene.hm_dm_prps
    rd_vl = prp_dt.dm_rd_vl
    pt_rd = prp_dt.pt_rd_vl
    
    pt_co_lst = []
    
    for i_idx in range(1, 4):
        if prp_dt.pt_md_st == 'MANUAL':
            u_dg = getattr(prp_dt, f"p{i_idx}_u_vl")
            v_dg = getattr(prp_dt, f"p{i_idx}_v_vl")
        else:
            random.seed(prp_dt.rn_sd_vl * 10 + i_idx)
            u_dg = random.uniform(0, 360)
            v_dg = random.uniform(0, 90)
            
        u_rd = math.radians(u_dg)
        v_rd = math.radians(v_dg)
        
        x_vl = rd_vl * math.sin(v_rd) * math.cos(u_rd)
        y_vl = rd_vl * math.sin(v_rd) * math.sin(u_rd)
        z_vl = rd_vl * math.cos(v_rd)
        co_vc = Vector((x_vl, y_vl, z_vl))
        pt_co_lst.append(co_vc)
        
        pt_ob = gt_or_cr_obj(f"{prfx_str}_pt_{i_idx}", 'MESH')
        if not pt_ob.data.vertices:
            bm_dt = bmesh.new()
            bmesh.ops.create_icosphere(bm_dt, subdivisions=2, radius=1.0)
            bm_dt.to_mesh(pt_ob.data)
            bm_dt.free()
        pt_ob.location = co_vc
        pt_ob.scale = (pt_rd, pt_rd, pt_rd)

    fc_ob = gt_or_cr_obj(f"{prfx_str}_fc", 'MESH')
    fc_ms = fc_ob.data
    fc_ms.clear_geometry()
    
    bm_dt = bmesh.new()
    b_vt = [bm_dt.verts.new(cv) for cv in pt_co_lst]
    try: bm_dt.faces.new((b_vt[0], b_vt[1], b_vt[2]))
    except Exception: pass
    bm_dt.to_mesh(fc_ms)
    bm_dt.free()
    
    mt_fc = gt_or_cr_mt(mt_fc_nm_str)
    st_mt_cl(mt_fc, prp_dt.fc_cl_vl)
    
    mt_pt = gt_or_cr_mt(mt_pt_nm_str)
    st_mt_cl(mt_pt, (prp_dt.fc_cl_vl[0], prp_dt.fc_cl_vl[1], prp_dt.fc_cl_vl[2], 1.0))
    
    for i_idx in range(1, 4):
        pt_ob_x = bpy.data.objects.get(f"{prfx_str}_pt_{i_idx}")
        if pt_ob_x:
            if not pt_ob_x.data.materials: pt_ob_x.data.materials.append(mt_pt)
            else: pt_ob_x.data.materials[0] = mt_pt

    if not fc_ms.materials: fc_ms.materials.append(mt_fc)
    else: fc_ms.materials[0] = mt_fc
    
    v1 = pt_co_lst[0]
    v2 = pt_co_lst[1]
    v3 = pt_co_lst[2]
    
    cr_vc = (v2 - v1).cross(v3 - v1)
    tr_ar = cr_vc.length * 0.5
    
    if cr_vc.length > 0.0001: nm_vc = cr_vc.normalized()
    else: nm_vc = Vector((0, 0, 1))
    
    cnt_pt = (v1 + v2 + v3) / 3.0
    if nm_vc.dot(cnt_pt) < 0:
        nm_vc = -nm_vc
        
    tg_pt = nm_vc * rd_vl
    fl_ar = math.pi * rd_vl * rd_vl
    ar_rt = (tr_ar / fl_ar) * 100.0 if fl_ar > 0 else 0.0
    
    ds_sn = nm_vc.dot(v1 - tg_pt)
    ds_vl = abs(ds_sn)
    ds_pt = tg_pt + nm_vc * ds_sn
    
    a, b, c = nm_vc.x, nm_vc.y, nm_vc.z
    d_tr = -(a * v1.x + b * v1.y + c * v1.z)
    d_tp = -(a * tg_pt.x + b * tg_pt.y + c * tg_pt.z)
    
    d_tr_abs = abs(nm_vc.dot(v1))
    d_p1 = v1.length
    d_p2 = v2.length
    d_p3 = v3.length
    d_tp_abs = tg_pt.length
    
    rt_p1 = d_p1 / d_tr_abs if d_tr_abs > 0.0001 else 0.0
    rt_p2 = d_p2 / d_tr_abs if d_tr_abs > 0.0001 else 0.0
    rt_p3 = d_p3 / d_tr_abs if d_tr_abs > 0.0001 else 0.0
    rt_tp = d_tp_abs / d_tr_abs if d_tr_abs > 0.0001 else 0.0
    
    n_dot_tg = nm_vc.dot(tg_pt)
    n_dot_v1 = nm_vc.dot(v1)
    n_dot_v2 = nm_vc.dot(v2)
    n_dot_v3 = nm_vc.dot(v3)
    
    t1 = n_dot_tg / n_dot_v1 if abs(n_dot_v1) > 0.0001 else 0.0
    t2 = n_dot_tg / n_dot_v2 if abs(n_dot_v2) > 0.0001 else 0.0
    t3 = n_dot_tg / n_dot_v3 if abs(n_dot_v3) > 0.0001 else 0.0
    
    ex_p1 = v1 * t1
    ex_p2 = v2 * t2
    ex_p3 = v3 * t3
    
    ex_d1 = ex_p1.length
    ex_d2 = ex_p2.length
    ex_d3 = ex_p3.length
    
    prp_dt.i_p1_pt = f"({v1.x:.1f}, {v1.y:.1f}, {v1.z:.1f})"
    prp_dt.i_p2_pt = f"({v2.x:.1f}, {v2.y:.1f}, {v2.z:.1f})"
    prp_dt.i_p3_pt = f"({v3.x:.1f}, {v3.y:.1f}, {v3.z:.1f})"
    prp_dt.i_tr_ar = f"{tr_ar:.1f}"
    prp_dt.i_fl_ar = f"{fl_ar:.1f}"
    prp_dt.i_ar_rt = f"{ar_rt:.1f} %"
    prp_dt.i_tg_pt = f"({tg_pt.x:.1f}, {tg_pt.y:.1f}, {tg_pt.z:.1f})"
    prp_dt.i_ds_vl = f"{ds_vl:.1f}"
    prp_dt.i_ds_pt = f"({ds_pt.x:.1f}, {ds_pt.y:.1f}, {ds_pt.z:.1f})"
    prp_dt.i_tr_eq = fmt_eq(a, b, c, d_tr)
    prp_dt.i_tp_eq = fmt_eq(a, b, c, d_tp)
    
    prp_dt.i_tr_ds0 = f"{d_tr_abs:.1f}"
    prp_dt.i_rt_p1 = f"{rt_p1:.1f}"
    prp_dt.i_rt_p2 = f"{rt_p2:.1f}"
    prp_dt.i_rt_p3 = f"{rt_p3:.1f}"
    prp_dt.i_rt_tp = f"{rt_tp:.1f}"
    prp_dt.i_ex_p1_pt = f"({ex_p1.x:.1f}, {ex_p1.y:.1f}, {ex_p1.z:.1f})"
    prp_dt.i_ex_p1_ds = f"{ex_d1:.1f}"
    prp_dt.i_ex_p2_pt = f"({ex_p2.x:.1f}, {ex_p2.y:.1f}, {ex_p2.z:.1f})"
    prp_dt.i_ex_p2_ds = f"{ex_d2:.1f}"
    prp_dt.i_ex_p3_pt = f"({ex_p3.x:.1f}, {ex_p3.y:.1f}, {ex_p3.z:.1f})"
    prp_dt.i_ex_p3_ds = f"{ex_d3:.1f}"
    
    tp_ob = gt_or_cr_obj(f"{prfx_str}_tg_pl", 'MESH')
    tp_ms = tp_ob.data
    tp_ms.clear_geometry()
    
    bm_tp = bmesh.new()
    hs = prp_dt.sq_sz_vl * 0.5
    vt1 = bm_tp.verts.new((-hs, -hs, 0))
    vt2 = bm_tp.verts.new(( hs, -hs, 0))
    vt3 = bm_tp.verts.new(( hs,  hs, 0))
    vt4 = bm_tp.verts.new((-hs,  hs, 0))
    bm_tp.faces.new((vt1, vt2, vt3, vt4))
    
    up_vc = Vector((0, 0, 1))
    rt_qt = up_vc.rotation_difference(nm_vc)
    bmesh.ops.transform(bm_tp, verts=bm_tp.verts, matrix=rt_qt.to_matrix().to_4x4())
    bmesh.ops.translate(bm_tp, verts=bm_tp.verts, vec=tg_pt)
    
    bm_tp.to_mesh(tp_ms)
    bm_tp.free()
    
    mt_tp = gt_or_cr_mt(mt_tp_nm_str)
    st_mt_cl(mt_tp, prp_dt.tp_cl_vl)
    if not tp_ms.materials: tp_ms.materials.append(mt_tp)
    else: tp_ms.materials[0] = mt_tp
    
    if ctx_env.view_layer: ctx_env.view_layer.update()
    return None

# --- 追加: TCS (トーラス・円錐・球体) 生成関数 ---
def rbld_tcs_fn():
    ctx_env = bpy.context
    if not ctx_env or not hasattr(ctx_env, "scene"): return None
    prp_dt = ctx_env.scene.hm_dm_prps
    
    tr_mj = prp_dt.tr_mj_rd
    tr_mn = prp_dt.tr_mn_rd
    cn_ht = prp_dt.cn_ht_vl
    sp_rd = prp_dt.sp_rd_vl
    
    loc = prp_dt.tcs_loc_vl
    rot = prp_dt.tcs_rot_vl
    rot_mat = Euler(rot).to_matrix()
    
    mt_tr = gt_or_cr_mt(f"{prfx_str}_mt_tr")
    st_mt_cl(mt_tr, prp_dt.tr_cl_vl)
    mt_cn = gt_or_cr_mt(f"{prfx_str}_mt_cn")
    st_mt_cl(mt_cn, prp_dt.cn_cl_vl)
    mt_sp = gt_or_cr_mt(f"{prfx_str}_mt_sp")
    st_mt_cl(mt_sp, prp_dt.sp_cl_vl)
    
    # トーラス
    tr_ob = gt_or_cr_obj(f"{prfx_str}_tr", 'MESH')
    tr_ms = tr_ob.data
    tr_ms.clear_geometry()
    
    segments = 48
    minor_segments = 16
    verts = []
    faces = []
    
    for i in range(segments):
        a1 = (i / segments) * 2.0 * math.pi
        c1, s1 = math.cos(a1), math.sin(a1)
        for j in range(minor_segments):
            a2 = (j / minor_segments) * 2.0 * math.pi
            c2, s2 = math.cos(a2), math.sin(a2)
            x = (tr_mj + tr_mn * c2) * c1
            y = (tr_mj + tr_mn * c2) * s1
            z = tr_mn * s2
            verts.append((x, y, z))
            
    for i in range(segments):
        for j in range(minor_segments):
            i_next = (i + 1) % segments
            j_next = (j + 1) % minor_segments
            v0 = i * minor_segments + j
            v1 = i_next * minor_segments + j
            v2 = i_next * minor_segments + j_next
            v3 = i * minor_segments + j_next
            faces.append((v0, v1, v2, v3))
            
    tr_ms.from_pydata(verts, [], faces)
    tr_ms.update()
    
    if not tr_ms.materials: tr_ms.materials.append(mt_tr)
    else: tr_ms.materials[0] = mt_tr
    
    tr_ob.location = loc
    tr_ob.rotation_euler = rot
    tr_ob.hide_viewport = not prp_dt.tr_shw_fl
    tr_ob.hide_render = not prp_dt.tr_shw_fl
    
    # 円錐
    cn_ob = gt_or_cr_obj(f"{prfx_str}_cn", 'MESH')
    cn_ms = cn_ob.data
    cn_ms.clear_geometry()
    bm_cn = bmesh.new()
    bmesh.ops.create_cone(
        bm_cn, cap_ends=True, cap_tris=True, segments=48,
        radius1=tr_mj, radius2=0, depth=cn_ht
    )
    bmesh.ops.translate(bm_cn, verts=bm_cn.verts, vec=(0, 0, cn_ht / 2))
    bm_cn.to_mesh(cn_ms)
    bm_cn.free()
    
    if not cn_ms.materials: cn_ms.materials.append(mt_cn)
    else: cn_ms.materials[0] = mt_cn
    
    cn_ob.location = loc
    cn_ob.rotation_euler = rot
    cn_ob.hide_viewport = not prp_dt.cn_shw_fl
    cn_ob.hide_render = not prp_dt.cn_shw_fl
    
    # 球体
    sp_ob = gt_or_cr_obj(f"{prfx_str}_sp", 'MESH')
    sp_ms = sp_ob.data
    sp_ms.clear_geometry()
    bm_sp = bmesh.new()
    bmesh.ops.create_uvsphere(bm_sp, u_segments=32, v_segments=16, radius=sp_rd)
    bm_sp.to_mesh(sp_ms)
    bm_sp.free()
    
    if not sp_ms.materials: sp_ms.materials.append(mt_sp)
    else: sp_ms.materials[0] = mt_sp
    
    sp_loc = Vector(loc) + rot_mat @ Vector((0, 0, cn_ht))
    sp_ob.location = sp_loc
    sp_ob.rotation_euler = rot
    sp_ob.hide_viewport = not prp_dt.sp_shw_fl
    sp_ob.hide_render = not prp_dt.sp_shw_fl
    
    if ctx_env.view_layer: ctx_env.view_layer.update()
    return None
# --------------------------------------------------

def dly_dm_cb(slf_dt, ctx_env):
    if not bpy.app.timers.is_registered(rbld_dm_fn): 
        bpy.app.timers.register(rbld_dm_fn, first_interval=0.01)
    dly_pt_cb(slf_dt, ctx_env)

def dly_pt_cb(slf_dt, ctx_env):
    if not bpy.app.timers.is_registered(rbld_pt_fc_fn): 
        bpy.app.timers.register(rbld_pt_fc_fn, first_interval=0.01)

def dly_tcs_cb(slf_dt, ctx_env):
    if not bpy.app.timers.is_registered(rbld_tcs_fn): 
        bpy.app.timers.register(rbld_tcs_fn, first_interval=0.01)

class Hm_Dm_Prps(PropertyGroup):
    dm_rd_vl: FloatProperty(name="ドーム半径", default=dflt_vls_dct["dm_rd_vl"], min=0.1, update=dly_dm_cb)
    pt_rd_vl: FloatProperty(name="球体半径", default=dflt_vls_dct["pt_rd_vl"], min=0.01, update=dly_pt_cb)
    lt_cn_vl: IntProperty(name="緯度の本数", default=dflt_vls_dct["lt_cn_vl"], min=1, max=100, update=dly_dm_cb)
    lt_th_vl: FloatProperty(name="緯度の太さ", default=dflt_vls_dct["lt_th_vl"], min=0.01, update=dly_dm_cb)
    lt_sg_vl: IntProperty(name="分割数(滑らかさ)", default=dflt_vls_dct["lt_sg_vl"], min=4, max=128, update=dly_dm_cb)
    ln_cn_vl: IntProperty(name="経度の本数", default=dflt_vls_dct["ln_cn_vl"], min=3, max=100, update=dly_dm_cb)
    ln_th_vl: FloatProperty(name="経度の太さ", default=dflt_vls_dct["ln_th_vl"], min=0.01, update=dly_dm_cb)
    ln_sg_vl: IntProperty(name="分割数(滑らかさ)", default=dflt_vls_dct["ln_sg_vl"], min=4, max=128, update=dly_dm_cb)
    dm_cl_vl: FloatVectorProperty(name="ドーム色", subtype='COLOR', size=4, default=dflt_vls_dct["dm_cl_vl"], min=0.0, max=1.0, update=dly_dm_cb)
    
    pt_md_st: EnumProperty(items=[('MANUAL', "手動指定", ""), ('RANDOM', "ランダム", "")], default=dflt_vls_dct["pt_md_st"], update=dly_pt_cb)
    rn_sd_vl: IntProperty(name="シード", default=dflt_vls_dct["rn_sd_vl"], update=dly_pt_cb)
    
    p1_u_vl: FloatProperty(name="U(経度)", default=dflt_vls_dct["p1_u_vl"], min=0, max=360, update=dly_pt_cb)
    p1_v_vl: FloatProperty(name="V(緯度)", default=dflt_vls_dct["p1_v_vl"], min=0, max=90, update=dly_pt_cb)
    p2_u_vl: FloatProperty(name="U(経度)", default=dflt_vls_dct["p2_u_vl"], min=0, max=360, update=dly_pt_cb)
    p2_v_vl: FloatProperty(name="V(緯度)", default=dflt_vls_dct["p2_v_vl"], min=0, max=90, update=dly_pt_cb)
    p3_u_vl: FloatProperty(name="U(経度)", default=dflt_vls_dct["p3_u_vl"], min=0, max=360, update=dly_pt_cb)
    p3_v_vl: FloatProperty(name="V(緯度)", default=dflt_vls_dct["p3_v_vl"], min=0, max=90, update=dly_pt_cb)
    fc_cl_vl: FloatVectorProperty(name="面の色", subtype='COLOR', size=4, default=dflt_vls_dct["fc_cl_vl"], min=0.0, max=1.0, update=dly_pt_cb)
    
    sq_sz_vl: FloatProperty(name="接平面のサイズ", default=dflt_vls_dct["sq_sz_vl"], min=0.1, update=dly_pt_cb)
    tp_cl_vl: FloatVectorProperty(name="接平面の色", subtype='COLOR', size=4, default=dflt_vls_dct["tp_cl_vl"], min=0.0, max=1.0, update=dly_pt_cb)

    # --- TCS Properties ---
    tcs_loc_vl: FloatVectorProperty(name="TCS Location", default=dflt_vls_dct["tcs_loc_vl"], update=dly_tcs_cb)
    tcs_rot_vl: FloatVectorProperty(name="TCS Rotation", subtype='EULER', default=dflt_vls_dct["tcs_rot_vl"], update=dly_tcs_cb)
    
    tr_mj_rd: FloatProperty(name="Torus Major Radius", default=dflt_vls_dct["tr_mj_rd"], min=0.01, update=dly_tcs_cb)
    tr_mn_rd: FloatProperty(name="Torus Minor Radius", default=dflt_vls_dct["tr_mn_rd"], min=0.01, update=dly_tcs_cb)
    cn_ht_vl: FloatProperty(name="Cone Height", default=dflt_vls_dct["cn_ht_vl"], min=0.01, update=dly_tcs_cb)
    sp_rd_vl: FloatProperty(name="Sphere Radius", default=dflt_vls_dct["sp_rd_vl"], min=0.01, update=dly_tcs_cb)
    
    tr_cl_vl: FloatVectorProperty(name="Torus Color", subtype='COLOR', size=4, default=dflt_vls_dct["tr_cl_vl"], min=0.0, max=1.0, update=dly_tcs_cb)
    cn_cl_vl: FloatVectorProperty(name="Cone Color", subtype='COLOR', size=4, default=dflt_vls_dct["cn_cl_vl"], min=0.0, max=1.0, update=dly_tcs_cb)
    sp_cl_vl: FloatVectorProperty(name="Sphere Color", subtype='COLOR', size=4, default=dflt_vls_dct["sp_cl_vl"], min=0.0, max=1.0, update=dly_tcs_cb)
    
    tr_shw_fl: BoolProperty(name="Torus Show", default=dflt_vls_dct["tr_shw_fl"], update=dly_tcs_cb)
    cn_shw_fl: BoolProperty(name="Cone Show", default=dflt_vls_dct["cn_shw_fl"], update=dly_tcs_cb)
    sp_shw_fl: BoolProperty(name="Sphere Show", default=dflt_vls_dct["sp_shw_fl"], update=dly_tcs_cb)
    # ----------------------

    i_p1_pt: StringProperty()
    i_p2_pt: StringProperty()
    i_p3_pt: StringProperty()
    i_tr_ar: StringProperty()
    i_fl_ar: StringProperty()
    i_ar_rt: StringProperty()
    i_tg_pt: StringProperty()
    i_ds_vl: StringProperty()
    i_ds_pt: StringProperty()
    i_tr_eq: StringProperty()
    i_tp_eq: StringProperty()
    
    i_tr_ds0: StringProperty()
    i_rt_p1: StringProperty()
    i_rt_p2: StringProperty()
    i_rt_p3: StringProperty()
    i_rt_tp: StringProperty()
    i_ex_p1_pt: StringProperty()
    i_ex_p1_ds: StringProperty()
    i_ex_p2_pt: StringProperty()
    i_ex_p2_ds: StringProperty()
    i_ex_p3_pt: StringProperty()
    i_ex_p3_ds: StringProperty()

class Op_Init_Cr_Dm(Operator):
    bl_idname = f"{prfx_str}_wm.init_cr_dm"
    bl_label = "ドームを新規作成"
    bl_description = "Hemisphere Dome と TCS オブジェクトをシーンに作成します"
    def execute(self, ctx_env):
        rbld_dm_fn()
        rbld_pt_fc_fn()
        rbld_tcs_fn()
        return {'FINISHED'}

class Op_Bld_Dm(Operator):
    bl_idname = f"{prfx_str}_wm.bld_dm"
    bl_label = "強制更新"
    def execute(self, ctx_env):
        rbld_dm_fn()
        rbld_pt_fc_fn()
        rbld_tcs_fn()
        return {'FINISHED'}

class Op_Rnd_Sd(Operator):
    bl_idname = f"{prfx_str}_wm.rnd_sd"
    bl_label = "ランダム化"
    def execute(self, ctx_env):
        ctx_env.scene.hm_dm_prps.rn_sd_vl = random.randint(1, 10000)
        return {'FINISHED'}

class Op_Cp_Inf(Operator):
    bl_idname = f"{prfx_str}_wm.cp_inf"
    bl_label = "ドーム幾何情報をコピー"
    def execute(self, ctx_env):
        prp_dt = ctx_env.scene.hm_dm_prps
        txt_lst = [
            "--- Hemisphere Dome Info ---",
            f"点1位置: {prp_dt.i_p1_pt}",
            f"点2位置: {prp_dt.i_p2_pt}",
            f"点3位置: {prp_dt.i_p3_pt}",
            f"三角形の面積: {prp_dt.i_tr_ar}",
            f"球体床面面積: {prp_dt.i_fl_ar}",
            f"面積比率: {prp_dt.i_ar_rt}",
            f"接点位置: {prp_dt.i_tg_pt}",
            f"接平面との最短距離: {prp_dt.i_ds_vl}",
            f"最短距離の投影位置: {prp_dt.i_ds_pt}",
            f"三角形平面の方程式: {prp_dt.i_tr_eq}",
            f"接平面の方程式: {prp_dt.i_tp_eq}",
            "--- 追加の幾何情報 ---",
            f"原点(0,0,0)から三角平面までの最短距離: {prp_dt.i_tr_ds0}",
            "[距離比率 (三角平面の最短距離=1)]",
            f"球体1への遠さ: {prp_dt.i_rt_p1}",
            f"球体2への遠さ: {prp_dt.i_rt_p2}",
            f"球体3への遠さ: {prp_dt.i_rt_p3}",
            f"接平面への遠さ: {prp_dt.i_rt_tp}",
            "[原点(0,0,0)から球体を延長した接平面交点]",
            f"球体1延長位置: {prp_dt.i_ex_p1_pt} (距離: {prp_dt.i_ex_p1_ds})",
            f"球体2延長位置: {prp_dt.i_ex_p2_pt} (距離: {prp_dt.i_ex_p2_ds})",
            f"球体3延長位置: {prp_dt.i_ex_p3_pt} (距離: {prp_dt.i_ex_p3_ds})",
            "----------------------------"
        ]
        ctx_env.window_manager.clipboard = "\n".join(txt_lst)
        self.report({'INFO'}, "ドーム幾何情報をコピーしました。")
        return {'FINISHED'}

# --- 追加: TCS情報コピー用オペレーター ---
class Op_Cp_Tcs_Inf(Operator):
    bl_idname = f"{prfx_str}_wm.cp_tcs_inf"
    bl_label = "TCS情報をコピー"
    def execute(self, ctx_env):
        prp_dt = ctx_env.scene.hm_dm_prps
        loc_str = f"X:{prp_dt.tcs_loc_vl[0]:.2f} Y:{prp_dt.tcs_loc_vl[1]:.2f} Z:{prp_dt.tcs_loc_vl[2]:.2f}"
        rot_str = f"X:{math.degrees(prp_dt.tcs_rot_vl[0]):.1f}° Y:{math.degrees(prp_dt.tcs_rot_vl[1]):.1f}° Z:{math.degrees(prp_dt.tcs_rot_vl[2]):.1f}°"
        
        txt_lst = [
            "--- TCS Info ---",
            "[Torus]",
            f"Location: {loc_str}",
            f"Rotation: {rot_str}",
            f"Major Radius: {prp_dt.tr_mj_rd:.2f}",
            f"Minor Radius: {prp_dt.tr_mn_rd:.2f}",
            "",
            "[Cone]",
            f"Location: {loc_str}",
            f"Rotation: {rot_str}",
            f"Radius: {prp_dt.tr_mj_rd:.2f}",
            f"Height: {prp_dt.cn_ht_vl:.2f}",
            "",
            "[Sphere]"
        ]
        sp_loc = Vector(prp_dt.tcs_loc_vl) + Euler(prp_dt.tcs_rot_vl).to_matrix() @ Vector((0, 0, prp_dt.cn_ht_vl))
        sp_loc_str = f"X:{sp_loc[0]:.2f} Y:{sp_loc[1]:.2f} Z:{sp_loc[2]:.2f}"
        txt_lst.append(f"Location: {sp_loc_str}")
        txt_lst.append(f"Radius: {prp_dt.sp_rd_vl:.2f}")
        
        ctx_env.window_manager.clipboard = "\n".join(txt_lst)
        self.report({'INFO'}, "TCS情報をコピーしました。")
        return {'FINISHED'}
# ----------------------------------------

class Op_Dtch_Obj(Operator):
    bl_idname = f"{prfx_str}_wm.dtch_obj"
    bl_label = "現在のオブジェクト群をアドオンから切り離し"
    def execute(self, ctx_env):
        ts_str = time.strftime("%H%M%S")
        clc_dt = bpy.data.collections.get(clc_nm_str)
        if not clc_dt: return {'CANCELLED'}
        
        clc_dt.name = f"Detached_Objects_{ts_str}"
        mat_map = {}
        for obj in clc_dt.objects:
            if obj.name.startswith(prfx_str): obj.name = obj.name.replace(prfx_str, f"Obj_{ts_str}")
            if obj.data and obj.data.name.startswith(prfx_str): obj.data.name = obj.data.name.replace(prfx_str, f"Data_{ts_str}")
            for i, slot in enumerate(obj.material_slots):
                if slot.material and slot.material.name.startswith(prfx_str):
                    orig_mat = slot.material
                    if orig_mat.name not in mat_map:
                        new_mat = orig_mat.copy()
                        new_mat.name = orig_mat.name.replace(prfx_str, f"Mat_{ts_str}")
                        mat_map[orig_mat.name] = new_mat
                    slot.material = mat_map[orig_mat.name]

        self.report({'INFO'}, f"オブジェクトを独立させました: {clc_dt.name}")
        return {'FINISHED'}

class Op_Rmv_Adn(Operator):
    bl_idname = f"{prfx_str}_wm.rmv_adn"
    bl_label = "アドオン削除"
    def execute(self, ctx_env): 
        unregister()
        return {'FINISHED'}

class Op_Xprt_Txt(Operator):
    bl_idname = f"{prfx_str}_wm.xprt_txt"
    bl_label = "設定をコードに書き出し"
    def execute(self, ctx_env):
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return {'CANCELLED'}
        prp_dt = ctx_env.scene.hm_dm_prps
        src_tx_st = ""
        
        fl_pt_st = globals().get("__file__")
        if fl_pt_st and fl_pt_st.endswith(".pyc"): fl_pt_st = fl_pt_st[:-1]
        if fl_pt_st and os.path.exists(fl_pt_st):
            try:
                with open(fl_pt_st, "r", encoding="utf-8") as f_ob: src_tx_st = f_ob.read()
            except Exception: pass
            
        if not src_tx_st:
            for tx_bk in bpy.data.texts:
                bd_st = tx_bk.as_string()
                if "# --- DFLT_VALS_START ---" in bd_st:
                    src_tx_st = bd_st
                    break
        
        if not src_tx_st:
            self.report({'ERROR'}, "元のソースコードが見つかりません。")
            return {'CANCELLED'}
        
        dc_st = "dflt_vls_dct = {\n"
        dc_st += f'    "dm_rd_vl": {prp_dt.dm_rd_vl},\n'
        dc_st += f'    "pt_rd_vl": {prp_dt.pt_rd_vl},\n'
        dc_st += f'    "lt_cn_vl": {prp_dt.lt_cn_vl},\n'
        dc_st += f'    "lt_th_vl": {prp_dt.lt_th_vl},\n'
        dc_st += f'    "lt_sg_vl": {prp_dt.lt_sg_vl},\n'
        dc_st += f'    "ln_cn_vl": {prp_dt.ln_cn_vl},\n'
        dc_st += f'    "ln_th_vl": {prp_dt.ln_th_vl},\n'
        dc_st += f'    "ln_sg_vl": {prp_dt.ln_sg_vl},\n'
        dc_st += f'    "dm_cl_vl": ({prp_dt.dm_cl_vl[0]:.4f}, {prp_dt.dm_cl_vl[1]:.4f}, {prp_dt.dm_cl_vl[2]:.4f}, {prp_dt.dm_cl_vl[3]:.4f}),\n'
        dc_st += f'    "fc_cl_vl": ({prp_dt.fc_cl_vl[0]:.4f}, {prp_dt.fc_cl_vl[1]:.4f}, {prp_dt.fc_cl_vl[2]:.4f}, {prp_dt.fc_cl_vl[3]:.4f}),\n'
        dc_st += f'    "pt_md_st": "{prp_dt.pt_md_st}",\n'
        dc_st += f'    "rn_sd_vl": {prp_dt.rn_sd_vl},\n'
        dc_st += f'    "p1_u_vl": {prp_dt.p1_u_vl},\n'
        dc_st += f'    "p1_v_vl": {prp_dt.p1_v_vl},\n'
        dc_st += f'    "p2_u_vl": {prp_dt.p2_u_vl},\n'
        dc_st += f'    "p2_v_vl": {prp_dt.p2_v_vl},\n'
        dc_st += f'    "p3_u_vl": {prp_dt.p3_u_vl},\n'
        dc_st += f'    "p3_v_vl": {prp_dt.p3_v_vl},\n'
        dc_st += f'    "sq_sz_vl": {prp_dt.sq_sz_vl},\n'
        dc_st += f'    "tp_cl_vl": ({prp_dt.tp_cl_vl[0]:.4f}, {prp_dt.tp_cl_vl[1]:.4f}, {prp_dt.tp_cl_vl[2]:.4f}, {prp_dt.tp_cl_vl[3]:.4f}),\n'
        
        # TCS 書き出し
        dc_st += f'    "tcs_loc_vl": ({prp_dt.tcs_loc_vl[0]:.4f}, {prp_dt.tcs_loc_vl[1]:.4f}, {prp_dt.tcs_loc_vl[2]:.4f}),\n'
        dc_st += f'    "tcs_rot_vl": ({prp_dt.tcs_rot_vl[0]:.4f}, {prp_dt.tcs_rot_vl[1]:.4f}, {prp_dt.tcs_rot_vl[2]:.4f}),\n'
        dc_st += f'    "tr_mj_rd": {prp_dt.tr_mj_rd},\n'
        dc_st += f'    "tr_mn_rd": {prp_dt.tr_mn_rd},\n'
        dc_st += f'    "cn_ht_vl": {prp_dt.cn_ht_vl},\n'
        dc_st += f'    "sp_rd_vl": {prp_dt.sp_rd_vl},\n'
        dc_st += f'    "tr_cl_vl": ({prp_dt.tr_cl_vl[0]:.4f}, {prp_dt.tr_cl_vl[1]:.4f}, {prp_dt.tr_cl_vl[2]:.4f}, {prp_dt.tr_cl_vl[3]:.4f}),\n'
        dc_st += f'    "cn_cl_vl": ({prp_dt.cn_cl_vl[0]:.4f}, {prp_dt.cn_cl_vl[1]:.4f}, {prp_dt.cn_cl_vl[2]:.4f}, {prp_dt.cn_cl_vl[3]:.4f}),\n'
        dc_st += f'    "sp_cl_vl": ({prp_dt.sp_cl_vl[0]:.4f}, {prp_dt.sp_cl_vl[1]:.4f}, {prp_dt.sp_cl_vl[2]:.4f}, {prp_dt.sp_cl_vl[3]:.4f}),\n'
        dc_st += f'    "tr_shw_fl": {prp_dt.tr_shw_fl},\n'
        dc_st += f'    "cn_shw_fl": {prp_dt.cn_shw_fl},\n'
        dc_st += f'    "sp_shw_fl": {prp_dt.sp_shw_fl},\n'
        
        dc_st += "}\n"
        
        ptn_rg = r"(# --- DFLT_VALS_START ---\n)(.*?)(# --- DFLT_VALS_END ---)"
        nw_sr_st = re.sub(ptn_rg, r"\g<1>" + dc_st + r"\g<3>", src_tx_st, flags=re.DOTALL)
        
        tx_nm_st = "Exp_HemiDome_TCS.py"
        nw_tx_bk = bpy.data.texts.get(tx_nm_st)
        if nw_tx_bk: nw_tx_bk.clear()
        else: nw_tx_bk = bpy.data.texts.new(tx_nm_st)
            
        nw_tx_bk.write(nw_sr_st)
        
        tx_shw_fl = False
        for ar_dt in ctx_env.screen.areas:
            if ar_dt.type == 'TEXT_EDITOR':
                ar_dt.spaces.active.text = nw_tx_bk
                tx_shw_fl = True
                break
                
        if tx_shw_fl: self.report({'INFO'}, f"書き出し成功: {tx_nm_st}")
        else: self.report({'INFO'}, f"内部データに保存: {tx_nm_st} (ワークスペースをScriptingなどに切り替えてください)")
            
        return {'FINISHED'}

class Pn_Bs(Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = bl_info["category"]

class Pn_Fc_003(Pn_Bs):
    bl_label = "1. 3点 UVコントロール"
    bl_idname = "VIEW3D_PT_hmdm1000_p1_003"
    bl_order = 2
    def draw(self, ctx_env):
        ly_dt = self.layout
        if not bpy.data.collections.get(clc_nm_str): return
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return
            
        prp_dt = ctx_env.scene.hm_dm_prps
        ly_dt.prop(prp_dt, "pt_md_st")
        ly_dt.separator()
        
        if prp_dt.pt_md_st == 'MANUAL':
            for i_idx in range(1, 4):
                bx_dt = ly_dt.box()
                bx_dt.label(text=f"球体 {i_idx}:")
                cl_dt = bx_dt.column(align=True)
                cl_dt.prop(prp_dt, f"p{i_idx}_u_vl")
                cl_dt.prop(prp_dt, f"p{i_idx}_v_vl")
        else:
            bx_dt = ly_dt.box()
            rw_dt = bx_dt.row(align=True)
            rw_dt.prop(prp_dt, "rn_sd_vl")
            rw_dt.operator(f"{prfx_str}_wm.rnd_sd", icon='FILE_REFRESH', text="")
            
        ly_dt.separator()
        ly_dt.prop(prp_dt, "fc_cl_vl")

class Pn_Dm_003(Pn_Bs):
    bl_label = "2. ドーム骨格・接平面設定"
    bl_idname = "VIEW3D_PT_hmdm1000_p2_003"
    bl_order = 1
    def draw(self, ctx_env):
        ly_dt = self.layout
        clc_dt = bpy.data.collections.get(clc_nm_str)
        if not clc_dt:
            ly_dt.operator(Op_Init_Cr_Dm.bl_idname, icon='ADD')
            return
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return
            
        prp_dt = ctx_env.scene.hm_dm_prps
        ly_dt.operator(f"{prfx_str}_wm.bld_dm", icon='MESH_UVSPHERE')
        ly_dt.separator()
        ly_dt.prop(prp_dt, "dm_rd_vl")
        ly_dt.prop(prp_dt, "pt_rd_vl")
        ly_dt.separator()
        
        cl_dt = ly_dt.column(align=True)
        cl_dt.label(text="緯度:")
        cl_dt.prop(prp_dt, "lt_cn_vl", text="本数")
        cl_dt.prop(prp_dt, "lt_th_vl", text="太さ")
        cl_dt.prop(prp_dt, "lt_sg_vl")
        
        cl_dt2 = ly_dt.column(align=True)
        cl_dt2.label(text="経度:")
        cl_dt2.prop(prp_dt, "ln_cn_vl", text="本数")
        cl_dt2.prop(prp_dt, "ln_th_vl", text="太さ")
        cl_dt2.prop(prp_dt, "ln_sg_vl")
        
        ly_dt.separator()
        ly_dt.prop(prp_dt, "dm_cl_vl")
        
        ly_dt.separator()
        bx_dt = ly_dt.box()
        bx_dt.label(text="接平面(正方形)の設定:")
        bx_dt.prop(prp_dt, "sq_sz_vl")
        bx_dt.prop(prp_dt, "tp_cl_vl")

class Pn_Inf_003(Pn_Bs):
    bl_label = "3. 幾何情報 (接平面・三角形)"
    bl_idname = "VIEW3D_PT_hmdm1000_p3_inf"
    bl_order = 3
    def draw(self, ctx_env):
        ly_dt = self.layout
        if not bpy.data.collections.get(clc_nm_str): return
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return
        prp_dt = ctx_env.scene.hm_dm_prps
        
        ly_dt.operator(Op_Cp_Inf.bl_idname, icon='COPYDOWN')
        bx_dt = ly_dt.box()
        cl_dt = bx_dt.column(align=True)
        
        cl_dt.label(text=f"点1: {prp_dt.i_p1_pt}")
        cl_dt.label(text=f"点2: {prp_dt.i_p2_pt}")
        cl_dt.label(text=f"点3: {prp_dt.i_p3_pt}")
        cl_dt.separator()
        cl_dt.label(text=f"三角面積: {prp_dt.i_tr_ar}")
        cl_dt.label(text=f"床面面積: {prp_dt.i_fl_ar}")
        cl_dt.label(text=f"面積割合: {prp_dt.i_ar_rt}")
        cl_dt.separator()
        cl_dt.label(text=f"接点: {prp_dt.i_tg_pt}")
        cl_dt.label(text=f"最短距離: {prp_dt.i_ds_vl}")
        cl_dt.label(text=f"投影位置: {prp_dt.i_ds_pt}")
        cl_dt.separator()
        cl_dt.label(text="[三角平面方程式]")
        cl_dt.label(text=prp_dt.i_tr_eq)
        cl_dt.label(text="[接平面方程式]")
        cl_dt.label(text=prp_dt.i_tp_eq)
        
        cl_dt.separator()
        cl_dt.label(text=f"原点(0,0,0)→三角平面 最短距離: {prp_dt.i_tr_ds0}")
        cl_dt.separator()
        cl_dt.label(text="[距離比率 (三角平面=1)]")
        cl_dt.label(text=f"球体1の遠さ: {prp_dt.i_rt_p1}")
        cl_dt.label(text=f"球体2の遠さ: {prp_dt.i_rt_p2}")
        cl_dt.label(text=f"球体3の遠さ: {prp_dt.i_rt_p3}")
        cl_dt.label(text=f"接平面の遠さ: {prp_dt.i_rt_tp}")
        cl_dt.separator()
        cl_dt.label(text="[原点(0,0,0)→球体 延長の接平面位置と距離]")
        cl_dt.label(text=f"延長1 位置: {prp_dt.i_ex_p1_pt}")
        cl_dt.label(text=f"延長1 距離: {prp_dt.i_ex_p1_ds}")
        cl_dt.label(text=f"延長2 位置: {prp_dt.i_ex_p2_pt}")
        cl_dt.label(text=f"延長2 距離: {prp_dt.i_ex_p2_ds}")
        cl_dt.label(text=f"延長3 位置: {prp_dt.i_ex_p3_pt}")
        cl_dt.label(text=f"延長3 距離: {prp_dt.i_ex_p3_ds}")

# --- 追加: TCS用パネル ---
class Pn_Tcs_003(Pn_Bs):
    bl_label = "4. トーラス・円錐・球体"
    bl_idname = "VIEW3D_PT_hmdm1000_p4_tcs"
    bl_order = 4
    def draw(self, ctx_env):
        ly_dt = self.layout
        clc_dt = bpy.data.collections.get(clc_nm_str)
        if not clc_dt: return
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return
        prp_dt = ctx_env.scene.hm_dm_prps
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Transform", icon='OBJECT_DATA')
        bx_dt.prop(prp_dt, "tcs_loc_vl", text="Location")
        bx_dt.prop(prp_dt, "tcs_rot_vl", text="Rotation")
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Colors", icon='COLOR')
        bx_dt.prop(prp_dt, "tr_cl_vl", text="Torus")
        bx_dt.prop(prp_dt, "cn_cl_vl", text="Cone")
        bx_dt.prop(prp_dt, "sp_cl_vl", text="Sphere")
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Shape Parameters", icon='MODIFIER')
        bx_dt.prop(prp_dt, "tr_mj_rd", text="Torus/Cone Radius")
        bx_dt.prop(prp_dt, "tr_mn_rd", text="Torus Thickness")
        bx_dt.prop(prp_dt, "cn_ht_vl", text="Cone Height")
        bx_dt.prop(prp_dt, "sp_rd_vl", text="Sphere Radius")
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Visibility", icon='RESTRICT_VIEW_OFF')
        rw_dt = bx_dt.row(align=True)
        rw_dt.prop(prp_dt, "tr_shw_fl", text="Torus", icon='HIDE_OFF' if prp_dt.tr_shw_fl else 'HIDE_ON', toggle=True)
        rw_dt.prop(prp_dt, "cn_shw_fl", text="Cone", icon='HIDE_OFF' if prp_dt.cn_shw_fl else 'HIDE_ON', toggle=True)
        rw_dt.prop(prp_dt, "sp_shw_fl", text="Sphere", icon='HIDE_OFF' if prp_dt.sp_shw_fl else 'HIDE_ON', toggle=True)

class Pn_Tcs_Inf(Pn_Bs):
    bl_label = "5. TCS Information"
    bl_idname = "VIEW3D_PT_hmdm1000_p5_tcs_inf"
    bl_order = 5
    def draw(self, ctx_env):
        ly_dt = self.layout
        if not bpy.data.collections.get(clc_nm_str): return
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return
        prp_dt = ctx_env.scene.hm_dm_prps
        
        ly_dt.operator(Op_Cp_Tcs_Inf.bl_idname, icon='COPYDOWN')
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Torus:", icon='MESH_TORUS')
        bx_dt.label(text=f"  Major R: {prp_dt.tr_mj_rd:.2f} / Minor r: {prp_dt.tr_mn_rd:.2f}")
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Cone:", icon='MESH_CONE')
        bx_dt.label(text=f"  Base R: {prp_dt.tr_mj_rd:.2f} / Height: {prp_dt.cn_ht_vl:.2f}")
        
        bx_dt = ly_dt.box()
        bx_dt.label(text="Sphere:", icon='MESH_UVSPHERE')
        sp_loc = Vector(prp_dt.tcs_loc_vl) + Euler(prp_dt.tcs_rot_vl).to_matrix() @ Vector((0, 0, prp_dt.cn_ht_vl))
        bx_dt.label(text=f"  Loc: X:{sp_loc[0]:.1f} Y:{sp_loc[1]:.1f} Z:{sp_loc[2]:.1f}")
        bx_dt.label(text=f"  Radius: {prp_dt.sp_rd_vl:.2f}")
# --------------------------------

class Pn_Rmv_003(Pn_Bs):
    bl_label = "6. アドオン管理"
    bl_idname = "VIEW3D_PT_hmdm1000_p6_rmv"
    bl_order = 6
    def draw(self, ctx_env):
        ly_dt = self.layout
        if not bpy.data.collections.get(clc_nm_str): return
        if not hasattr(ctx_env.scene, "hm_dm_prps"): return
            
        ly_dt.operator(f"{prfx_str}_wm.dtch_obj", icon='UNLINKED')
        ly_dt.separator()
        ly_dt.operator(f"{prfx_str}_wm.xprt_txt", icon='TEXT')
        ly_dt.separator()
        ly_dt.operator(f"{prfx_str}_wm.rmv_adn", icon='CANCEL')

cls_lst = [
    Hm_Dm_Prps, Op_Init_Cr_Dm, Op_Bld_Dm, Op_Rnd_Sd, Op_Cp_Inf, Op_Cp_Tcs_Inf,
    Op_Dtch_Obj, Op_Rmv_Adn, Op_Xprt_Txt, 
    Pn_Fc_003, Pn_Dm_003, Pn_Inf_003, Pn_Tcs_003, Pn_Tcs_Inf, Pn_Rmv_003
]

def st_up_in_fn():
    ok_fl = False
    try:
        ctx_env = bpy.context
        if not ctx_env or not ctx_env.window_manager:
            return 0.2
            
        for wn_dt in ctx_env.window_manager.windows:
            sc_dt = wn_dt.screen
            for ar_dt in sc_dt.areas:
                if ar_dt.type != 'VIEW_3D': continue
                for sp_dt in ar_dt.spaces:
                    if sp_dt.type != 'VIEW_3D': continue
                    sp_dt.show_region_ui = True
                    sp_dt.shading.type = 'MATERIAL'
                    sp_dt.overlay.show_overlays = True
                    sp_dt.shading.show_xray = False
                    sp_dt.shading.light = 'STUDIO'
                    sp_dt.shading.color_type = 'MATERIAL'
                    ok_fl = True
    except Exception: pass
    if ok_fl: return None
    return 0.2

@persistent
def ld_hn_fn(dm_dt):
    if not bpy.app.timers.is_registered(st_up_in_fn):
        bpy.app.timers.register(st_up_in_fn, first_interval=0.2)

def register():
    for cl_ob in cls_lst: bpy.utils.register_class(cl_ob)
    bpy.types.Scene.hm_dm_prps = PointerProperty(type=Hm_Dm_Prps)
    bpy.app.handlers.load_post.append(ld_hn_fn)
    if not bpy.app.timers.is_registered(st_up_in_fn):
        bpy.app.timers.register(st_up_in_fn, first_interval=0.2)

def unregister():
    if hasattr(bpy.types.Scene, 'hm_dm_prps'): del bpy.types.Scene.hm_dm_prps
    for cl_ob in reversed(cls_lst):
        try: bpy.utils.unregister_class(cl_ob)
        except RuntimeError: pass
    if ld_hn_fn in bpy.app.handlers.load_post:
        bpy.app.handlers.load_post.remove(ld_hn_fn)
    if bpy.app.timers.is_registered(st_up_in_fn):
        bpy.app.timers.unregister(st_up_in_fn)

if __name__ == "__main__": register()