bl_info = {
"name": "Sine Wave Generator (Space & Time Trajectory)",
"author": "ChatGPT",
"version": (2, 5, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar (N Panel)",
"description": "進行波、時間軌跡(複数)、t=ax波面などをリアルタイムで描画します。",
"category": "3D View",
}
import bpy
import math
# ===================================
# 共通定義 & 初期設定
# ===================================
ADDON_TITLE = "Sine Wave Generator"
PREFIX = "sine_wave"
OBJ_PREFIX = "SINE_WAVE_"
SINE_AMPLITUDE_DEFAULT = 2.0
SINE_FREQUENCY_DEFAULT = 1.0
SINE_LENGTH_DEFAULT = 10.0
SINE_TIME_MAX_DEFAULT = 10.0
SINE_TIME_DEFAULT = 1.0
SINE_DELAY_DEFAULT = 0.0
SINE_RESOLUTION_DEFAULT = 128
SINE_WAVE_SPEED_X_DEFAULT = 1.0
SINE_LOC_X_DEFAULT = 0.0
SINE_LOC_Y_DEFAULT = 0.0
SINE_LOC_Z_DEFAULT = 0.0
INIT_COLOR = (0.5, 0.5, 0.5, 0.8)
MAIN_COLOR = (0.2, 0.6, 1.0, 1.0)
TRAJ1_COLOR = (1.0, 0.8, 0.2, 1.0) # 軌跡1の色 (オレンジ)
TRAJ2_COLOR = (1.0, 0.4, 0.8, 1.0) # 軌跡2の色 (ピンク)
TX_COLOR = (0.2, 1.0, 0.5, 1.0)
# ===================================
# データクリーンアップ関数
# ===================================
def clear_sine_wave_data():
for obj in list(bpy.data.objects):
if obj.name.startswith(OBJ_PREFIX):
bpy.data.objects.remove(obj, do_unlink=True)
for crv in list(bpy.data.curves):
if crv.name.startswith(OBJ_PREFIX):
bpy.data.curves.remove(crv)
for mat in list(bpy.data.materials):
if mat.name.startswith(OBJ_PREFIX):
bpy.data.materials.remove(mat)
# ===================================
# 共通の変位計算 & 波形生成ロジック
# ===================================
def calculate_z_displacement(x, t, props):
f = props.frequency
v = props.wave_speed_x
# 1. 位相(Phase)の計算
if v == 0.0:
phase = 2.0 * math.pi * f * (t - props.delay)
else:
if props.wave_direction == 'SPACE':
phase = 2.0 * math.pi * f * (x / v - (t - props.delay))
else:
phase = 2.0 * math.pi * f * ((t - props.delay) - x / v)
# 2. 振幅(Amplitude)の計算
amp = props.amplitude
if props.taper_x and props.length > 0.0:
amp = props.amplitude * (x / props.length)
return amp * math.sin(phase) + SINE_LOC_Z_DEFAULT
def create_curve_object(name, thickness, color, collection):
curve_data = bpy.data.curves.new(name=name, type='CURVE')
curve_data.dimensions = '3D'
curve_data.bevel_depth = thickness
curve_data.bevel_resolution = 4
spline = curve_data.splines.new(type='POLY')
obj = bpy.data.objects.new(name, curve_data)
collection.objects.link(obj)
mat = bpy.data.materials.new(name=f"{name}_Mat")
mat.use_nodes = True
mat.blend_method = 'BLEND'
bsdf = mat.node_tree.nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs['Base Color'].default_value = color
if len(color) == 4 and color[3] < 1.0:
bsdf.inputs['Alpha'].default_value = color[3]
obj.data.materials.append(mat)
return spline
def generate_waves(context):
props = context.scene.sine_wave_props
clear_sine_wave_data()
coll_name = "SineWave_Collection"
if coll_name in bpy.data.collections:
coll = bpy.data.collections[coll_name]
else:
coll = bpy.data.collections.new(coll_name)
context.scene.collection.children.link(coll)
# 1. 初期波形 (t=0)
if props.init_show:
spline_init = create_curve_object(f"{OBJ_PREFIX}Init_t0", props.init_thickness, props.init_color, coll)
spline_init.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length
y = 0.0 + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, 0.0, props)
spline_init.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 2. メインの波 (現在の時間)
if props.main_show:
spline_main = create_curve_object(f"{OBJ_PREFIX}Main", props.thickness, props.color_main, coll)
spline_main.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length
y = props.time_t + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, props.time_t, props)
spline_main.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 3. 時間軌跡 1 (任意のX座標)
if props.traj1_show and props.time_max > 0.0:
spline_traj1 = create_curve_object(f"{OBJ_PREFIX}Trajectory1", props.traj1_thickness, props.traj1_color, coll)
spline_traj1.points.add(props.resolution - 1)
for i in range(props.resolution):
tau = (i / (props.resolution - 1)) * props.time_max
x_local = props.traj1_x
y = tau + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, tau, props)
spline_traj1.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 4. 時間軌跡 2 (任意のX座標)
if props.traj2_show and props.time_max > 0.0:
spline_traj2 = create_curve_object(f"{OBJ_PREFIX}Trajectory2", props.traj2_thickness, props.traj2_color, coll)
spline_traj2.points.add(props.resolution - 1)
for i in range(props.resolution):
tau = (i / (props.resolution - 1)) * props.time_max
x_local = props.traj2_x
y = tau + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, tau, props)
spline_traj2.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 5. t=ax 波面
if props.tx_show:
spline_tx = create_curve_object(f"{OBJ_PREFIX}Wave_tx", props.tx_thickness, props.tx_color, coll)
spline_tx.points.add(props.resolution - 1)
for i in range(props.resolution):
if props.tx_slope_a == 0.0:
max_tx_length = props.length
else:
max_tx_length = min(props.length, props.time_max / props.tx_slope_a)
x_local = (i / (props.resolution - 1)) * max_tx_length
t_local = props.tx_slope_a * x_local
y = t_local + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, t_local, props)
spline_tx.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# ===================================
# リアルタイム反映のコールバック
# ===================================
def update_waves_cb(self, context):
if context and hasattr(context.scene, 'sine_wave_props'):
generate_waves(context)
# ===================================
# プロパティ定義
# ===================================
class SineWaveProperties(bpy.types.PropertyGroup):
amplitude: bpy.props.FloatProperty(name="最大振幅 (Z軸)", default=SINE_AMPLITUDE_DEFAULT, update=update_waves_cb)
frequency: bpy.props.FloatProperty(name="時間振動数 f (回/単位時間)", default=SINE_FREQUENCY_DEFAULT, update=update_waves_cb)
length: bpy.props.FloatProperty(name="空間の長さ (X最大)", default=SINE_LENGTH_DEFAULT, update=update_waves_cb)
time_max: bpy.props.FloatProperty(name="時間の長さ (Y最大)", default=SINE_TIME_MAX_DEFAULT, update=update_waves_cb)
time_t: bpy.props.FloatProperty(
name="現在の時間 t",
default=SINE_TIME_DEFAULT,
soft_min=0.0,
soft_max=10.0,
update=update_waves_cb
)
wave_speed_x: bpy.props.FloatProperty(name="X方向速度 (v)", default=SINE_WAVE_SPEED_X_DEFAULT, soft_min=-10.0, soft_max=10.0, update=update_waves_cb)
delay: bpy.props.FloatProperty(name="開始時間の遅れ", default=SINE_DELAY_DEFAULT, update=update_waves_cb)
resolution: bpy.props.IntProperty(name="解像度", default=SINE_RESOLUTION_DEFAULT, min=2, update=update_waves_cb)
wave_direction: bpy.props.EnumProperty(
name="波の開始基準",
items=[
('SPACE', "空間 (X軸) で山から開始", "t=0の初期波において、X軸正の方向へ『山』から始まります"),
('TIME', "時間 (Y軸) で山から開始", "原点X=0において、時間が進むと『山』から湧き出します")
],
default='SPACE',
update=update_waves_cb
)
taper_x: bpy.props.BoolProperty(
name="X軸方向へ徐々に山を高くする",
description="X=0で振幅を0にし、Xが進むにつれて山の高さを最大振幅まで徐々に大きくします",
default=False,
update=update_waves_cb
)
init_show: bpy.props.BoolProperty(name="初期波 (t=0)", default=True, update=update_waves_cb)
init_thickness: bpy.props.FloatProperty(name="初期波の太さ", default=0.05, min=0.0, update=update_waves_cb)
init_color: bpy.props.FloatVectorProperty(name="初期波の色", subtype='COLOR', size=4, default=INIT_COLOR, min=0.0, max=1.0, update=update_waves_cb)
main_show: bpy.props.BoolProperty(name="時間波形 (t=t)", default=True, update=update_waves_cb)
thickness: bpy.props.FloatProperty(name="時間波形の太さ", default=0.15, min=0.0, update=update_waves_cb)
color_main: bpy.props.FloatVectorProperty(name="時間波形の色", subtype='COLOR', size=4, default=MAIN_COLOR, min=0.0, max=1.0, update=update_waves_cb)
# ======== 変更点: 軌跡1 と 軌跡2 に拡張 ========
traj1_show: bpy.props.BoolProperty(name="軌跡 1", default=True, update=update_waves_cb)
traj1_x: bpy.props.FloatProperty(name="軌跡1 X位置", default=0.0, update=update_waves_cb)
traj1_thickness: bpy.props.FloatProperty(name="軌跡1の太さ", default=0.1, min=0.0, update=update_waves_cb)
traj1_color: bpy.props.FloatVectorProperty(name="軌跡1の色", subtype='COLOR', size=4, default=TRAJ1_COLOR, min=0.0, max=1.0, update=update_waves_cb)
traj2_show: bpy.props.BoolProperty(name="軌跡 2", default=True, update=update_waves_cb)
traj2_x: bpy.props.FloatProperty(name="軌跡2 X位置", default=2.0, update=update_waves_cb)
traj2_thickness: bpy.props.FloatProperty(name="軌跡2の太さ", default=0.1, min=0.0, update=update_waves_cb)
traj2_color: bpy.props.FloatVectorProperty(name="軌跡2の色", subtype='COLOR', size=4, default=TRAJ2_COLOR, min=0.0, max=1.0, update=update_waves_cb)
# ===============================================
tx_show: bpy.props.BoolProperty(name="t=ax 波面", default=True, update=update_waves_cb)
tx_slope_a: bpy.props.FloatProperty(name="係数 (a)", default=1.0, min=0.0, max=1.0, update=update_waves_cb)
tx_thickness: bpy.props.FloatProperty(name="t=ax 波の太さ", default=0.12, min=0.0, update=update_waves_cb)
tx_color: bpy.props.FloatVectorProperty(name="t=ax 波の色", subtype='COLOR', size=4, default=TX_COLOR, min=0.0, max=1.0, update=update_waves_cb)
# ===================================
# UI パネル
# ===================================
class SINE_WAVE_PT_panel(bpy.types.Panel):
bl_label = ADDON_TITLE
bl_idname = f"{PREFIX}_PT_panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "SineWave"
def draw(self, context):
layout = self.layout
props = context.scene.sine_wave_props
# 1. 波の形と方向
box = layout.box()
box.label(text="波の形・方向の設定", icon='MOD_WAVE')
box.prop(props, "wave_direction")
box.prop(props, "taper_x")
# 2. 基本設定
box = layout.box()
box.label(text="物理パラメータ", icon='VIEW_ORTHO')
box.prop(props, "amplitude")
box.prop(props, "frequency")
box.prop(props, "length")
box.prop(props, "time_max")
box.separator()
box.prop(props, "time_t", slider=True)
box.prop(props, "wave_speed_x")
box.prop(props, "delay")
box.prop(props, "resolution")
# 3. 波の表示切り替え
box2 = layout.box()
box2.label(text="波の表示切り替え (ON/OFF)", icon='RESTRICT_VIEW_OFF')
row1 = box2.row(align=True)
row1.prop(props, "init_show", toggle=True, text="t=0")
row1.prop(props, "main_show", toggle=True, text="t=t")
row1.prop(props, "tx_show", toggle=True, text="t=ax")
row2 = box2.row(align=True)
row2.prop(props, "traj1_show", toggle=True, text="軌跡1")
row2.prop(props, "traj2_show", toggle=True, text="軌跡2")
# 4. 見た目の詳細設定
box3 = layout.box()
box3.label(text="色・太さ・位置の設定", icon='MATERIAL')
if props.init_show:
row = box3.row()
row.label(text="初期 (t=0):")
row.prop(props, "init_thickness", text="太さ")
row.prop(props, "init_color", text="")
if props.main_show:
row = box3.row()
row.label(text=f"現在 (t={props.time_t:.1f}):")
row.prop(props, "thickness", text="太さ")
row.prop(props, "color_main", text="")
# ======== 変更点: 軌跡1 と 軌跡2 のUI ========
if props.traj1_show:
box3.separator()
row = box3.row(align=True)
row.label(text="軌跡1:")
row.prop(props, "traj1_x", text="X座標")
row.prop(props, "traj1_thickness", text="太さ")
row.prop(props, "traj1_color", text="")
if props.traj2_show:
row = box3.row(align=True)
row.label(text="軌跡2:")
row.prop(props, "traj2_x", text="X座標")
row.prop(props, "traj2_thickness", text="太さ")
row.prop(props, "traj2_color", text="")
# ============================================
if props.tx_show:
box3.separator()
row_a = box3.row()
row_a.label(text="波面 (t=ax):")
row_a.prop(props, "tx_slope_a", text="係数 a", slider=True)
row = box3.row()
row.label(text="")
row.prop(props, "tx_thickness", text="太さ")
row.prop(props, "tx_color", text="")
# ===================================
# 登録処理
# ===================================
def setup_viewport_ui():
for window in bpy.context.window_manager.windows:
for area in window.screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
space.shading.type = 'MATERIAL'
space.show_region_ui = True
if bpy.context.scene:
generate_waves(bpy.context)
return None
classes = (SineWaveProperties, SINE_WAVE_PT_panel)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.sine_wave_props = bpy.props.PointerProperty(type=SineWaveProperties)
bpy.app.timers.register(setup_viewport_ui, first_interval=0.1)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.sine_wave_props
clear_sine_wave_data()
if __name__ == "__main__":
register()
x=0 の t軸 描画を
x=2 とかの t軸描画を 追加して
bl_info = {
"name": "Sine Wave Generator (Space & Time Trajectory)",
"author": "ChatGPT",
"version": (2, 4, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar (N Panel)",
"description": "進行波、時間軌跡、t=ax波面などをリアルタイムで描画します。",
"category": "3D View",
}
import bpy
import math
# ===================================
# 共通定義 & 初期設定
# ===================================
ADDON_TITLE = "Sine Wave Generator"
PREFIX = "sine_wave"
OBJ_PREFIX = "SINE_WAVE_"
SINE_AMPLITUDE_DEFAULT = 2.0
SINE_FREQUENCY_DEFAULT = 1.0
SINE_LENGTH_DEFAULT = 10.0
SINE_TIME_MAX_DEFAULT = 10.0
SINE_TIME_DEFAULT = 1.0
SINE_DELAY_DEFAULT = 0.0
SINE_RESOLUTION_DEFAULT = 128
SINE_WAVE_SPEED_X_DEFAULT = 1.0
SINE_LOC_X_DEFAULT = 0.0
SINE_LOC_Y_DEFAULT = 0.0
SINE_LOC_Z_DEFAULT = 0.0
INIT_COLOR = (0.5, 0.5, 0.5, 0.8)
MAIN_COLOR = (0.2, 0.6, 1.0, 1.0)
TRAJ_COLOR = (1.0, 0.8, 0.2, 1.0)
TX_COLOR = (0.2, 1.0, 0.5, 1.0)
# ===================================
# データクリーンアップ関数
# ===================================
def clear_sine_wave_data():
"""このアドオンで生成したオブジェクト、カーブ、マテリアルを全て削除"""
for obj in list(bpy.data.objects):
if obj.name.startswith(OBJ_PREFIX):
bpy.data.objects.remove(obj, do_unlink=True)
for crv in list(bpy.data.curves):
if crv.name.startswith(OBJ_PREFIX):
bpy.data.curves.remove(crv)
for mat in list(bpy.data.materials):
if mat.name.startswith(OBJ_PREFIX):
bpy.data.materials.remove(mat)
# ===================================
# 共通の変位計算 & 波形生成ロジック
# ===================================
def calculate_z_displacement(x, t, props):
f = props.frequency
v = props.wave_speed_x
# 1. 位相(Phase)の計算
if v == 0.0:
phase = 2.0 * math.pi * f * (t - props.delay)
else:
if props.wave_direction == 'SPACE':
# t=0 のときに x>0 方向が「山(上)」から始まる式
phase = 2.0 * math.pi * f * (x / v - (t - props.delay))
else:
# x=0 において、時間が進むと「山(上)」から湧き出す式
phase = 2.0 * math.pi * f * ((t - props.delay) - x / v)
# 2. 振幅(Amplitude)の計算
amp = props.amplitude
if props.taper_x and props.length > 0.0:
# x=0で振幅0、x=lengthで最大振幅になるよう徐々に山を高くする
amp = props.amplitude * (x / props.length)
return amp * math.sin(phase) + SINE_LOC_Z_DEFAULT
def create_curve_object(name, thickness, color, collection):
curve_data = bpy.data.curves.new(name=name, type='CURVE')
curve_data.dimensions = '3D'
curve_data.bevel_depth = thickness
curve_data.bevel_resolution = 4
spline = curve_data.splines.new(type='POLY')
obj = bpy.data.objects.new(name, curve_data)
collection.objects.link(obj)
mat = bpy.data.materials.new(name=f"{name}_Mat")
mat.use_nodes = True
mat.blend_method = 'BLEND'
bsdf = mat.node_tree.nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs['Base Color'].default_value = color
if len(color) == 4 and color[3] < 1.0:
bsdf.inputs['Alpha'].default_value = color[3]
obj.data.materials.append(mat)
return spline
def generate_waves(context):
props = context.scene.sine_wave_props
clear_sine_wave_data()
coll_name = "SineWave_Collection"
if coll_name in bpy.data.collections:
coll = bpy.data.collections[coll_name]
else:
coll = bpy.data.collections.new(coll_name)
context.scene.collection.children.link(coll)
# 1. 初期波形 (t=0)
if props.init_show:
spline_init = create_curve_object(f"{OBJ_PREFIX}Init_t0", props.init_thickness, props.init_color, coll)
spline_init.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length
y = 0.0 + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, 0.0, props)
spline_init.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 2. メインの波 (現在の時間)
if props.main_show:
spline_main = create_curve_object(f"{OBJ_PREFIX}Main", props.thickness, props.color_main, coll)
spline_main.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length
y = props.time_t + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, props.time_t, props)
spline_main.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 3. 時間軌跡 (x=0)
if props.traj_show and props.time_max > 0.0:
spline_traj = create_curve_object(f"{OBJ_PREFIX}Trajectory", props.traj_thickness, props.traj_color, coll)
spline_traj.points.add(props.resolution - 1)
for i in range(props.resolution):
tau = (i / (props.resolution - 1)) * props.time_max
x_local = 0.0
y = tau + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, tau, props)
spline_traj.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# 4. t=ax 波面
if props.tx_show:
spline_tx = create_curve_object(f"{OBJ_PREFIX}Wave_tx", props.tx_thickness, props.tx_color, coll)
spline_tx.points.add(props.resolution - 1)
for i in range(props.resolution):
if props.tx_slope_a == 0.0:
max_tx_length = props.length
else:
max_tx_length = min(props.length, props.time_max / props.tx_slope_a)
x_local = (i / (props.resolution - 1)) * max_tx_length
t_local = props.tx_slope_a * x_local
y = t_local + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, t_local, props)
spline_tx.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# ===================================
# リアルタイム反映のコールバック
# ===================================
def update_waves_cb(self, context):
if context and hasattr(context.scene, 'sine_wave_props'):
generate_waves(context)
# ===================================
# プロパティ定義
# ===================================
class SineWaveProperties(bpy.types.PropertyGroup):
amplitude: bpy.props.FloatProperty(name="最大振幅 (Z軸)", default=SINE_AMPLITUDE_DEFAULT, update=update_waves_cb)
frequency: bpy.props.FloatProperty(name="時間振動数 f (回/単位時間)", default=SINE_FREQUENCY_DEFAULT, update=update_waves_cb)
length: bpy.props.FloatProperty(name="空間の長さ (X最大)", default=SINE_LENGTH_DEFAULT, update=update_waves_cb)
time_max: bpy.props.FloatProperty(name="時間の長さ (Y最大)", default=SINE_TIME_MAX_DEFAULT, update=update_waves_cb)
time_t: bpy.props.FloatProperty(
name="現在の時間 t",
default=SINE_TIME_DEFAULT,
soft_min=0.0,
soft_max=10.0,
update=update_waves_cb
)
wave_speed_x: bpy.props.FloatProperty(name="X方向速度 (v)", default=SINE_WAVE_SPEED_X_DEFAULT, soft_min=-10.0, soft_max=10.0, update=update_waves_cb)
delay: bpy.props.FloatProperty(name="開始時間の遅れ", default=SINE_DELAY_DEFAULT, update=update_waves_cb)
resolution: bpy.props.IntProperty(name="解像度", default=SINE_RESOLUTION_DEFAULT, min=2, update=update_waves_cb)
# ======== 追加: 波の形・方向の設定 ========
wave_direction: bpy.props.EnumProperty(
name="波の開始基準",
items=[
('SPACE', "空間 (X軸) で山から開始", "t=0の初期波において、X軸正の方向へ『山』から始まります"),
('TIME', "時間 (Y軸) で山から開始", "原点X=0において、時間が進むと『山』から湧き出します")
],
default='SPACE',
update=update_waves_cb
)
taper_x: bpy.props.BoolProperty(
name="X軸方向へ徐々に山を高くする",
description="X=0で振幅を0にし、Xが進むにつれて山の高さを最大振幅まで徐々に大きくします",
default=False,
update=update_waves_cb
)
# =========================================
init_show: bpy.props.BoolProperty(name="初期波 (t=0)", default=True, update=update_waves_cb)
init_thickness: bpy.props.FloatProperty(name="初期波の太さ", default=0.05, min=0.0, update=update_waves_cb)
init_color: bpy.props.FloatVectorProperty(name="初期波の色", subtype='COLOR', size=4, default=INIT_COLOR, min=0.0, max=1.0, update=update_waves_cb)
main_show: bpy.props.BoolProperty(name="時間波形 (t=t)", default=True, update=update_waves_cb)
thickness: bpy.props.FloatProperty(name="時間波形の太さ", default=0.15, min=0.0, update=update_waves_cb)
color_main: bpy.props.FloatVectorProperty(name="時間波形の色", subtype='COLOR', size=4, default=MAIN_COLOR, min=0.0, max=1.0, update=update_waves_cb)
traj_show: bpy.props.BoolProperty(name="時間軌跡 (x=0)", default=True, update=update_waves_cb)
traj_thickness: bpy.props.FloatProperty(name="軌跡の太さ", default=0.1, min=0.0, update=update_waves_cb)
traj_color: bpy.props.FloatVectorProperty(name="軌跡の色", subtype='COLOR', size=4, default=TRAJ_COLOR, min=0.0, max=1.0, update=update_waves_cb)
tx_show: bpy.props.BoolProperty(name="t=ax 波面", default=True, update=update_waves_cb)
tx_slope_a: bpy.props.FloatProperty(name="係数 (a)", default=1.0, min=0.0, max=1.0, update=update_waves_cb)
tx_thickness: bpy.props.FloatProperty(name="t=ax 波の太さ", default=0.12, min=0.0, update=update_waves_cb)
tx_color: bpy.props.FloatVectorProperty(name="t=ax 波の色", subtype='COLOR', size=4, default=TX_COLOR, min=0.0, max=1.0, update=update_waves_cb)
# ===================================
# UI パネル
# ===================================
class SINE_WAVE_PT_panel(bpy.types.Panel):
bl_label = ADDON_TITLE
bl_idname = f"{PREFIX}_PT_panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "SineWave"
def draw(self, context):
layout = self.layout
props = context.scene.sine_wave_props
# 1. 波の形と方向 (新規追加)
box = layout.box()
box.label(text="波の形・方向の設定", icon='MOD_WAVE')
box.prop(props, "wave_direction")
box.prop(props, "taper_x")
# 2. 基本設定
box = layout.box()
box.label(text="物理パラメータ", icon='VIEW_ORTHO')
box.prop(props, "amplitude")
box.prop(props, "frequency")
box.prop(props, "length")
box.prop(props, "time_max")
box.separator()
box.prop(props, "time_t", slider=True)
box.prop(props, "wave_speed_x")
box.prop(props, "delay")
box.prop(props, "resolution")
# 3. 波の表示切り替え
box2 = layout.box()
box2.label(text="波の表示切り替え (ON/OFF)", icon='RESTRICT_VIEW_OFF')
row = box2.row(align=True)
row.prop(props, "init_show", toggle=True, text="t=0")
row.prop(props, "main_show", toggle=True, text="t=t")
row.prop(props, "traj_show", toggle=True, text="x=0")
row.prop(props, "tx_show", toggle=True, text="t=ax")
# 4. 見た目の詳細設定
box3 = layout.box()
box3.label(text="色と太さの設定", icon='MATERIAL')
if props.init_show:
row = box3.row()
row.label(text="初期 (t=0):")
row.prop(props, "init_thickness", text="太さ")
row.prop(props, "init_color", text="")
if props.main_show:
row = box3.row()
row.label(text=f"現在 (t={props.time_t:.1f}):")
row.prop(props, "thickness", text="太さ")
row.prop(props, "color_main", text="")
if props.traj_show:
row = box3.row()
row.label(text="軌跡 (x=0):")
row.prop(props, "traj_thickness", text="太さ")
row.prop(props, "traj_color", text="")
if props.tx_show:
box3.separator()
row_a = box3.row()
row_a.label(text="波面 (t=ax):")
row_a.prop(props, "tx_slope_a", text="係数 a", slider=True)
row = box3.row()
row.label(text="")
row.prop(props, "tx_thickness", text="太さ")
row.prop(props, "tx_color", text="")
# ===================================
# 登録処理
# ===================================
def setup_viewport_ui():
for window in bpy.context.window_manager.windows:
for area in window.screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
space.shading.type = 'MATERIAL'
space.show_region_ui = True
if bpy.context.scene:
generate_waves(bpy.context)
return None
classes = (SineWaveProperties, SINE_WAVE_PT_panel)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.sine_wave_props = bpy.props.PointerProperty(type=SineWaveProperties)
bpy.app.timers.register(setup_viewport_ui, first_interval=0.1)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.sine_wave_props
clear_sine_wave_data()
if __name__ == "__main__":
register()
bl_info = {
"name": "Sine Wave Generator (Space & Time Trajectory)",
"author": "ChatGPT",
"version": (1, 6, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar (N Panel)",
"description": "進行波、x=0の時間軌跡、t=xの波面などを描画します。",
"category": "3D View",
}
import bpy
import math
# ===================================
# 共通定義 & 初期設定
# ===================================
ADDON_TITLE = "Sine Wave Generator"
PREFIX = "sine_wave"
# 数値
SINE_AMPLITUDE_DEFAULT = 2.0
SINE_FREQUENCY_DEFAULT = 1.0
SINE_LENGTH_DEFAULT = 10.0
SINE_TIME_DEFAULT = 1.0 # デフォルトを t=1.0 に変更
SINE_DELAY_DEFAULT = 0.0
SINE_RESOLUTION_DEFAULT = 128
SINE_WAVE_SPEED_X_DEFAULT = 1.0
# 位置オフセット
SINE_LOC_X_DEFAULT = 0.0
SINE_LOC_Y_DEFAULT = 0.0
SINE_LOC_Z_DEFAULT = 0.0
# デフォルトの色
INIT_COLOR = (0.5, 0.5, 0.5, 0.8) # t=0 波形 (グレー)
MAIN_COLOR = (0.2, 0.6, 1.0, 1.0) # t=1 波形 (青)
TRAJ_COLOR = (1.0, 0.8, 0.2, 1.0) # x=0 軌跡 (黄色)
TX_COLOR = (0.2, 1.0, 0.5, 1.0) # t=x 波面 (緑)
# ===================================
# 共通の変位計算関数
# ===================================
def calculate_z_displacement(x, t, props):
"""
空間(x)と時間(t)から、Z軸方向の変位を計算する共通関数。
※ 波の式を (v*t - x) とすることで、t>0 (時間が進む) のとき、
x=0 における最初の変位が z>0 (山) になります。
"""
phase_x = props.wave_speed_x * (t - props.delay) - x
return props.amplitude * math.sin(props.frequency * phase_x) + SINE_LOC_Z_DEFAULT
# ===================================
# プロパティ定義
# ===================================
class SineWaveProperties(bpy.types.PropertyGroup):
# 基本パラメータ
amplitude: bpy.props.FloatProperty(name="振幅高さ (Z軸)", default=SINE_AMPLITUDE_DEFAULT)
frequency: bpy.props.FloatProperty(name="周波数 (k)", default=SINE_FREQUENCY_DEFAULT)
length: bpy.props.FloatProperty(name="波の長さ (X軸)", default=SINE_LENGTH_DEFAULT, description="x=0 から +X方向への描画距離")
time_t: bpy.props.FloatProperty(name="時間 t (Y座標)", default=SINE_TIME_DEFAULT, description="時間波形を描画する時刻")
wave_speed_x: bpy.props.FloatProperty(name="X方向速度 (v)", default=SINE_WAVE_SPEED_X_DEFAULT, soft_min=-10.0, soft_max=10.0)
delay: bpy.props.FloatProperty(name="開始時間の遅れ", default=SINE_DELAY_DEFAULT)
resolution: bpy.props.IntProperty(name="解像度", default=SINE_RESOLUTION_DEFAULT, min=2)
# 1. 初期波 (t=0)
init_show: bpy.props.BoolProperty(name="初期波 (t=0) を描画", default=True)
init_thickness: bpy.props.FloatProperty(name="初期波の太さ", default=0.05, min=0.0)
init_color: bpy.props.FloatVectorProperty(name="初期波の色", subtype='COLOR', size=4, default=INIT_COLOR, min=0.0, max=1.0)
# 2. メイン波 (t=t)
main_show: bpy.props.BoolProperty(name="時間波形 (t=t) を描画", default=True)
thickness: bpy.props.FloatProperty(name="時間波形の太さ", default=0.15, min=0.0)
color_main: bpy.props.FloatVectorProperty(name="時間波形の色", subtype='COLOR', size=4, default=MAIN_COLOR, min=0.0, max=1.0)
# 3. 時間軌跡 (x=0)
traj_show: bpy.props.BoolProperty(name="時間軌跡 (x=0) を描画", default=True)
traj_thickness: bpy.props.FloatProperty(name="軌跡の太さ", default=0.1, min=0.0)
traj_color: bpy.props.FloatVectorProperty(name="軌跡の色", subtype='COLOR', size=4, default=TRAJ_COLOR, min=0.0, max=1.0)
# 4. t=x 波面
tx_show: bpy.props.BoolProperty(name="t=x 波面を描画", default=True)
tx_thickness: bpy.props.FloatProperty(name="t=x 波の太さ", default=0.12, min=0.0)
tx_color: bpy.props.FloatVectorProperty(name="t=x 波の色", subtype='COLOR', size=4, default=TX_COLOR, min=0.0, max=1.0)
# ===================================
# オペレーター (生成処理)
# ===================================
class SINE_WAVE_OT_generate(bpy.types.Operator):
bl_idname = f"{PREFIX}.generate"
bl_label = "波と軌跡を生成"
def create_curve_object(self, name, thickness, color, collection):
"""カーブとマテリアルを生成するヘルパー関数"""
curve_data = bpy.data.curves.new(name=name, type='CURVE')
curve_data.dimensions = '3D'
curve_data.bevel_depth = thickness
curve_data.bevel_resolution = 4
spline = curve_data.splines.new(type='POLY')
obj = bpy.data.objects.new(name, curve_data)
collection.objects.link(obj)
mat = bpy.data.materials.new(name=f"{name}_Mat")
mat.use_nodes = True
mat.blend_method = 'BLEND'
bsdf = mat.node_tree.nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs['Base Color'].default_value = color
if len(color) == 4 and color[3] < 1.0:
bsdf.inputs['Alpha'].default_value = color[3]
obj.data.materials.append(mat)
return spline
def execute(self, context):
props = context.scene.sine_wave_props
collection = context.collection
# ------------------------------------------------
# 1. 初期波形 (t=0 の空間波形: y = 0)
# ------------------------------------------------
if props.init_show:
spline_init = self.create_curve_object("Wave_Init_t0", props.init_thickness, props.init_color, collection)
spline_init.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length # x > 0 基準
y = 0.0 + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, 0.0, props)
spline_init.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# ------------------------------------------------
# 2. メインの波 (空間波形: y = time_t)
# ------------------------------------------------
if props.main_show:
spline_main = self.create_curve_object(f"Wave_Main_t{props.time_t:.1f}", props.thickness, props.color_main, collection)
spline_main.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length # x > 0 基準
y = props.time_t + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, props.time_t, props)
spline_main.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# ------------------------------------------------
# 3. 時間軌跡 (x=0 の時間軸変位: y = 0 to time_t)
# ------------------------------------------------
if props.traj_show and props.time_t > 0.0:
spline_traj = self.create_curve_object("Trajectory_x0", props.traj_thickness, props.traj_color, collection)
spline_traj.points.add(props.resolution - 1)
for i in range(props.resolution):
tau = (i / (props.resolution - 1)) * props.time_t
x_local = 0.0 # x=0 に固定
y = tau + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, tau, props)
spline_traj.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
# ------------------------------------------------
# 4. t=x 波面 (xとtが連動する斜めの面上の波: y = x)
# ------------------------------------------------
if props.tx_show:
spline_tx = self.create_curve_object("Wave_t_equals_x", props.tx_thickness, props.tx_color, collection)
spline_tx.points.add(props.resolution - 1)
for i in range(props.resolution):
x_local = (i / (props.resolution - 1)) * props.length
t_local = x_local # t = x
y = t_local + SINE_LOC_Y_DEFAULT
z = calculate_z_displacement(x_local, t_local, props)
spline_tx.points[i].co = (x_local + SINE_LOC_X_DEFAULT, y, z, 1.0)
return {'FINISHED'}
# ===================================
# UI パネル
# ===================================
class SINE_WAVE_PT_panel(bpy.types.Panel):
bl_label = ADDON_TITLE
bl_idname = f"{PREFIX}_PT_panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "SineWave"
def draw(self, context):
layout = self.layout
props = context.scene.sine_wave_props
# 基本設定
box = layout.box()
box.label(text="基本設定", icon='VIEW_ORTHO')
box.prop(props, "amplitude")
box.prop(props, "frequency")
box.prop(props, "length")
box.prop(props, "time_t")
box.prop(props, "wave_speed_x")
box.prop(props, "delay")
box.prop(props, "resolution")
# 各種描画のオンオフと見た目
box2 = layout.box()
box2.label(text="波の描画設定", icon='OUTLINER_OB_CURVE')
# 1. 初期波 (t=0)
box2.prop(props, "init_show", icon='TIME')
if props.init_show:
row = box2.row()
row.prop(props, "init_thickness", text="太さ")
row.prop(props, "init_color", text="")
# 2. メイン波 (t=t)
box2.prop(props, "main_show", text=f"時間波形 (t={props.time_t:.1f}) を描画", icon='MOD_WAVE')
if props.main_show:
row = box2.row()
row.prop(props, "thickness", text="太さ")
row.prop(props, "color_main", text="")
# 3. 軌跡 (x=0)
box2.prop(props, "traj_show", icon='ANIM')
if props.traj_show:
row = box2.row()
row.prop(props, "traj_thickness", text="太さ")
row.prop(props, "traj_color", text="")
# 4. t=x 波面
box2.prop(props, "tx_show", icon='CON_TRANSLIKE')
if props.tx_show:
row = box2.row()
row.prop(props, "tx_thickness", text="太さ")
row.prop(props, "tx_color", text="")
layout.separator()
row = layout.row()
row.scale_y = 1.5
row.operator(SINE_WAVE_OT_generate.bl_idname, text="波と軌跡を作成", icon='PLAY')
def setup_viewport_ui():
for window in bpy.context.window_manager.windows:
for area in window.screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
space.shading.type = 'MATERIAL'
space.show_region_ui = True
return None
classes = (
SineWaveProperties,
SINE_WAVE_OT_generate,
SINE_WAVE_PT_panel,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.sine_wave_props = bpy.props.PointerProperty(type=SineWaveProperties)
bpy.app.timers.register(setup_viewport_ui, first_interval=0.1)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.sine_wave_props
if __name__ == "__main__":
register()