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),
# 法線ベクトル矢印 default values
"arw_len_vl": 40.0,
"arw_th_vl": 0.5,
"arw_cl_vl": (1.0, 0.1, 0.1, 1.0),
"arw_shw_fl": True,
# 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, 4, 1),
"blender": (5, 0, 0),
"category": " 1000[ Hemisphere Dome ] ",
"description": "Smoothness control, Geometric Info, Arrow & 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
# --- 法線ベクトル矢印の生成 ---
arw_ob = gt_or_cr_obj(f"{prfx_str}_arw", 'MESH')
arw_ms = arw_ob.data
arw_ms.clear_geometry()
if prp_dt.arw_shw_fl:
bm_arw = bmesh.new()
arw_len = prp_dt.arw_len_vl
arw_th = prp_dt.arw_th_vl
cn_ht = arw_th * 4.0
if cn_ht > arw_len * 0.5:
cn_ht = arw_len * 0.5
cy_len = arw_len - cn_ht
# シリンダー部分 (軸)
bmesh.ops.create_cone(
bm_arw, cap_ends=True, cap_tris=True, segments=16,
radius1=arw_th, radius2=arw_th, depth=cy_len
)
bmesh.ops.translate(bm_arw, verts=bm_arw.verts, vec=(0, 0, cy_len / 2))
# コーン部分 (矢尻)
verts_start = len(bm_arw.verts)
bmesh.ops.create_cone(
bm_arw, cap_ends=True, cap_tris=True, segments=16,
radius1=arw_th * 2.5, radius2=0, depth=cn_ht
)
verts_cone = bm_arw.verts[verts_start:]
bmesh.ops.translate(bm_arw, verts=verts_cone, vec=(0, 0, cy_len + cn_ht / 2))
# 法線ベクトル方向へ回転
bmesh.ops.transform(bm_arw, verts=bm_arw.verts, matrix=rt_qt.to_matrix().to_4x4())
bm_arw.to_mesh(arw_ms)
bm_arw.free()
mt_arw = gt_or_cr_mt(f"{prfx_str}_mt_arw")
st_mt_cl(mt_arw, prp_dt.arw_cl_vl)
if not arw_ms.materials: arw_ms.materials.append(mt_arw)
else: arw_ms.materials[0] = mt_arw
arw_ob.hide_viewport = not prp_dt.arw_shw_fl
arw_ob.hide_render = not prp_dt.arw_shw_fl
# ------------------------------
if ctx_env.view_layer: ctx_env.view_layer.update()
return None
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)
# --- 法線ベクトル矢印 Properties ---
arw_len_vl: FloatProperty(name="矢印の長さ", default=dflt_vls_dct["arw_len_vl"], min=0.1, update=dly_pt_cb)
arw_th_vl: FloatProperty(name="矢印の太さ", default=dflt_vls_dct["arw_th_vl"], min=0.01, update=dly_pt_cb)
arw_cl_vl: FloatVectorProperty(name="矢印の色", subtype='COLOR', size=4, default=dflt_vls_dct["arw_cl_vl"], min=0.0, max=1.0, update=dly_pt_cb)
arw_shw_fl: BoolProperty(name="矢印の表示", default=dflt_vls_dct["arw_shw_fl"], 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'}
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 = "Unregister Addon"
bl_description = "このアドオンを無効化し、UIから消去します"
def execute(self, context):
# 即座にunregisterせず、タイマーを用いて安全に解除処理を遅延実行する
bpy.app.timers.register(unregister, first_interval=0.01)
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'
# 矢印 書き出し
dc_st += f' "arw_len_vl": {prp_dt.arw_len_vl},\n'
dc_st += f' "arw_th_vl": {prp_dt.arw_th_vl},\n'
dc_st += f' "arw_cl_vl": ({prp_dt.arw_cl_vl[0]:.4f}, {prp_dt.arw_cl_vl[1]:.4f}, {prp_dt.arw_cl_vl[2]:.4f}, {prp_dt.arw_cl_vl[3]:.4f}),\n'
dc_st += f' "arw_shw_fl": {prp_dt.arw_shw_fl},\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")
# 法線ベクトル矢印UI
bx_dt = ly_dt.box()
rw_dt = bx_dt.row(align=True)
rw_dt.prop(prp_dt, "arw_shw_fl", text="", icon='HIDE_OFF' if prp_dt.arw_shw_fl else 'HIDE_ON')
rw_dt.label(text="法線ベクトル矢印の設定:")
bx_dt.prop(prp_dt, "arw_len_vl")
bx_dt.prop(prp_dt, "arw_th_vl")
bx_dt.prop(prp_dt, "arw_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}")
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_Data_Mgmt(Pn_Bs):
bl_label = "6. データ管理"
bl_idname = "VIEW3D_PT_hmdm1000_p6_data"
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')
# --- 修正: 提供されたアドオン削除専用パネル(常に表示) ---
class Pn_Rmv_003(Pn_Bs):
bl_label = "アドオン削除"
bl_idname = "VIEW3D_PT_hmdm1000_p7_rmv"
bl_options = {'DEFAULT_CLOSED'}
bl_order = 10
def draw(self, context):
layout = self.layout
box = layout.box()
box.label(text="このアドオンを消去します", icon='ERROR')
box.operator(Op_Rmv_Adn.bl_idname, 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_Data_Mgmt, 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()
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),
# 法線ベクトル矢印 default values
"arw_len_vl": 40.0,
"arw_th_vl": 0.5,
"arw_cl_vl": (1.0, 0.1, 0.1, 1.0),
"arw_shw_fl": True,
# 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, 4, 0),
"blender": (5, 0, 0),
"category": " 1000[ Hemisphere Dome ] ",
"description": "Smoothness control, Geometric Info, Arrow & 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
# --- 法線ベクトル矢印の生成 ---
arw_ob = gt_or_cr_obj(f"{prfx_str}_arw", 'MESH')
arw_ms = arw_ob.data
arw_ms.clear_geometry()
if prp_dt.arw_shw_fl:
bm_arw = bmesh.new()
arw_len = prp_dt.arw_len_vl
arw_th = prp_dt.arw_th_vl
cn_ht = arw_th * 4.0
if cn_ht > arw_len * 0.5:
cn_ht = arw_len * 0.5
cy_len = arw_len - cn_ht
# シリンダー部分 (軸)
bmesh.ops.create_cone(
bm_arw, cap_ends=True, cap_tris=True, segments=16,
radius1=arw_th, radius2=arw_th, depth=cy_len
)
bmesh.ops.translate(bm_arw, verts=bm_arw.verts, vec=(0, 0, cy_len / 2))
# コーン部分 (矢尻)
verts_start = len(bm_arw.verts)
bmesh.ops.create_cone(
bm_arw, cap_ends=True, cap_tris=True, segments=16,
radius1=arw_th * 2.5, radius2=0, depth=cn_ht
)
verts_cone = bm_arw.verts[verts_start:]
bmesh.ops.translate(bm_arw, verts=verts_cone, vec=(0, 0, cy_len + cn_ht / 2))
# 法線ベクトル方向へ回転
bmesh.ops.transform(bm_arw, verts=bm_arw.verts, matrix=rt_qt.to_matrix().to_4x4())
bm_arw.to_mesh(arw_ms)
bm_arw.free()
mt_arw = gt_or_cr_mt(f"{prfx_str}_mt_arw")
st_mt_cl(mt_arw, prp_dt.arw_cl_vl)
if not arw_ms.materials: arw_ms.materials.append(mt_arw)
else: arw_ms.materials[0] = mt_arw
arw_ob.hide_viewport = not prp_dt.arw_shw_fl
arw_ob.hide_render = not prp_dt.arw_shw_fl
# ------------------------------
if ctx_env.view_layer: ctx_env.view_layer.update()
return None
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)
# --- 法線ベクトル矢印 Properties ---
arw_len_vl: FloatProperty(name="矢印の長さ", default=dflt_vls_dct["arw_len_vl"], min=0.1, update=dly_pt_cb)
arw_th_vl: FloatProperty(name="矢印の太さ", default=dflt_vls_dct["arw_th_vl"], min=0.01, update=dly_pt_cb)
arw_cl_vl: FloatVectorProperty(name="矢印の色", subtype='COLOR', size=4, default=dflt_vls_dct["arw_cl_vl"], min=0.0, max=1.0, update=dly_pt_cb)
arw_shw_fl: BoolProperty(name="矢印の表示", default=dflt_vls_dct["arw_shw_fl"], 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'}
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'
# 矢印 書き出し
dc_st += f' "arw_len_vl": {prp_dt.arw_len_vl},\n'
dc_st += f' "arw_th_vl": {prp_dt.arw_th_vl},\n'
dc_st += f' "arw_cl_vl": ({prp_dt.arw_cl_vl[0]:.4f}, {prp_dt.arw_cl_vl[1]:.4f}, {prp_dt.arw_cl_vl[2]:.4f}, {prp_dt.arw_cl_vl[3]:.4f}),\n'
dc_st += f' "arw_shw_fl": {prp_dt.arw_shw_fl},\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")
# 法線ベクトル矢印UI
bx_dt = ly_dt.box()
rw_dt = bx_dt.row(align=True)
rw_dt.prop(prp_dt, "arw_shw_fl", text="", icon='HIDE_OFF' if prp_dt.arw_shw_fl else 'HIDE_ON')
rw_dt.label(text="法線ベクトル矢印の設定:")
bx_dt.prop(prp_dt, "arw_len_vl")
bx_dt.prop(prp_dt, "arw_th_vl")
bx_dt.prop(prp_dt, "arw_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}")
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()
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()