blender Million 2026

https://posfie.com/@timekagura?sort=0&page=1

https://x.com/zionadchat

基本オブジェクト操作

作業場

作業場 (1)

時空図 20260320版
<https://www.notion.so/20260320-329f5dacaf4380e0b192fb449676db13>

rapture_20260320153707.png

# Copied at: 20260320 15:33:26
import bpy
import bmesh
import math
import webbrowser
from bpy.props import FloatVectorProperty, FloatProperty, BoolProperty, PointerProperty, StringProperty, IntProperty
from bpy.types import Operator, Panel, PropertyGroup
from mathutils import Vector
from datetime import datetime

# ==============================================================================
#  設定エリア & ID管理
# ==============================================================================

PREFIX = "Minkowski20260320_Fixed"
TAB_NAME = "   [ Minkowski ]   "

# スクリプトが自分自身を特定するための固有ID
SOURCE_ID_TAG = "### ZIONAD_SOURCE_ID: MINKOWSKI_2026_03_20_STABLE_V8 ###"

bl_info = {
    "name": f"zionad 520 [ Minkowski Gen ] {PREFIX}",
    "author": "zionadchat",
    "version": (1, 6, 2),
    "blender": (5, 0, 0),
    "location": "3D View > Sidebar",
    "description": "Stable Minkowski Diagram with Intersections",
    "category": "3D View",
}

OP_PREFIX = PREFIX.lower()
PROPS_NAME = f"{PREFIX}_props"

ADDON_LINKS = (
    {"label": "時空図 20260320版", "url": "<https://www.notion.so/20260320-329f5dacaf4380e0b192fb449676db13>"},
    {"label": "Code Copy Template", "url": "<https://www.notion.so/Code-copy-20260221>"},
    {"label": "Theory Background", "url": "<https://www.notion.so/Einstein-from-20260119>"},
)

# ==============================================================================
#  デフォルト値設定 (Copy Script機能でここが動的に書き換わります)
# ==============================================================================
# <BEGIN_DICT>
CURRENT_DEFAULTS = {
    "show_preview": True,
    "beta": 0.6000, "thickness": 0.0300,
    "precision": 4,
    "vis_axis": True, "col_axis": (0.1495, 0.1495, 0.1495, 1.0),
    "vis_light": True, "col_light": (0.1109, 0.0904, 0.0, 0.5),
    "vis_thyp": True, "col_thyp": (1.0, 0.2, 0.2, 1.0),
    "vis_shyp": True, "col_shyp": (0.2, 0.2, 1.0, 1.0),
    "vis_ctp": True, "col_ctp": (0.0, 1.0, 0.2, 1.0),
    "vis_xp": True, "col_xp": (1.0, 0.0, 1.0, 1.0),
}
# <END_DICT>

# ==============================================================================
#  描画 & マテリアル制御
# ==============================================================================

def cleanup_minkowski_data():
    """PREFIXが付いた全データブロックを完全に削除"""
    for obj in bpy.data.objects:
        if obj.name.startswith(PREFIX):
            bpy.data.objects.remove(obj, do_unlink=True)
    
    for data in[bpy.data.curves, bpy.data.meshes, bpy.data.materials]:
        for block in data:
            if block.name.startswith(PREFIX) and block.users == 0:
                data.remove(block)

def get_or_create_material(name_suffix, color):
    mat_name = f"{PREFIX}_Mat_{name_suffix}"
    mat = bpy.data.materials.get(mat_name) or bpy.data.materials.new(mat_name)
    mat.use_nodes = True
    mat.blend_method = 'BLEND'
    
    if mat.use_nodes:
        nodes = mat.node_tree.nodes
        bsdf = nodes.get("Principled BSDF") or nodes.new("ShaderNodeBsdfPrincipled")
        bsdf.inputs['Base Color'].default_value = color
        if 'Alpha' in bsdf.inputs:
            bsdf.inputs['Alpha'].default_value = color[3]
    return mat

def draw_minkowski_diagram(context):
    props = getattr(context.scene, PROPS_NAME, None)
    if not props: return

    cleanup_minkowski_data()
    if not props.show_preview: return

    target_col = context.scene.collection
    beta = props.beta
    thickness = props.thickness
    gamma = 1 / math.sqrt(1 - beta**2) if beta < 0.999 else 20.0

    def add_line(name, points, color, is_visible):
        if not is_visible: return
        curve = bpy.data.curves.new(f"{PREFIX}_Curve_{name}", type='CURVE')
        curve.dimensions = '3D'
        curve.fill_mode = 'FULL'
        curve.bevel_depth = thickness
        
        spline = curve.splines.new('POLY')
        spline.points.add(len(points) - 1)
        for i, p in enumerate(points):
            spline.points[i].co = (p[0], p[1], 0, 1)
            
        obj = bpy.data.objects.new(f"{PREFIX}_{name}", curve)
        target_col.objects.link(obj)
        obj.data.materials.append(get_or_create_material(name, color))

    # 1. 静止系軸
    add_line("Axis_S",[(0, -6), (0, 6)], props.col_axis, props.vis_axis)
    add_line("Axis_X",[(-6, 0), (6, 0)], props.col_axis, props.vis_axis)
    
    # 2. 光円錐
    add_line("Light_P",[(-6, -6), (6, 6)], props.col_light, props.vis_light)
    add_line("Light_M",[(-6, 6), (6, -6)], props.col_light, props.vis_light)

    # 3. 不変双曲線 (sinh/cosh)
    q_range =[i * 0.1 - 2.5 for i in range(51)]
    add_line("Hyp_T",[(math.sinh(q), math.cosh(q)) for q in q_range], props.col_thyp, props.vis_thyp)
    add_line("Hyp_S",[(math.cosh(q), math.sinh(q)) for q in q_range], props.col_shyp, props.vis_shyp)

    # 4. 運動系軸
    add_line("Axis_CTP",[(-6*beta, -6), (6*beta, 6)], props.col_ctp, props.vis_ctp)
    add_line("Axis_XP",[(-6, -6*beta), (6, 6*beta)], props.col_xp, props.vis_xp)

    # 5. メモリ校正用マーカー (交点)
    if props.vis_axis or props.vis_ctp:
        m_mat = get_or_create_material("Marker", props.col_axis)
        for loc in[(beta*gamma, gamma), (gamma, beta*gamma)]:
            bpy.ops.mesh.primitive_uv_sphere_add(radius=thickness*2.5, location=(loc[0], loc[1], 0))
            marker = context.active_object
            marker.name = f"{PREFIX}_Marker"
            marker.data.materials.append(m_mat)

# ==============================================================================
#  タイマー更新制御
# ==============================================================================
_timer = None
def trigger_update():
    global _timer
    _timer = None
    if bpy.context and bpy.context.scene:
        draw_minkowski_diagram(bpy.context)
    return None

def on_property_update(self, context):
    global _timer
    if _timer:
        try: bpy.app.timers.unregister(_timer)
        except: pass
    _timer = bpy.app.timers.register(trigger_update, first_interval=0.03)

# ==============================================================================
#  PROPERTIES
# ==============================================================================

class PG_MinkowskiProps(PropertyGroup):
    show_preview: BoolProperty(name="Live Update", default=CURRENT_DEFAULTS['show_preview'], update=on_property_update)
    beta: FloatProperty(name="Beta (v/c)", default=CURRENT_DEFAULTS['beta'], min=0, max=0.999, update=on_property_update)
    thickness: FloatProperty(name="Thickness", default=CURRENT_DEFAULTS['thickness'], min=0.001, max=0.2, update=on_property_update)
    precision: IntProperty(name="Decimals", default=CURRENT_DEFAULTS.get('precision', 4), min=0, max=9, description="Decimal places for intersections")
    
    vis_axis: BoolProperty(default=CURRENT_DEFAULTS['vis_axis'], update=on_property_update)
    col_axis: FloatVectorProperty(name="Main Axes", subtype='COLOR', size=4, min=0, max=1, default=CURRENT_DEFAULTS['col_axis'], update=on_property_update)
    
    vis_light: BoolProperty(default=CURRENT_DEFAULTS['vis_light'], update=on_property_update)
    col_light: FloatVectorProperty(name="Light Cone", subtype='COLOR', size=4, min=0, max=1, default=CURRENT_DEFAULTS['col_light'], update=on_property_update)
    
    vis_thyp: BoolProperty(default=CURRENT_DEFAULTS['vis_thyp'], update=on_property_update)
    col_thyp: FloatVectorProperty(name="Timelike Hyp", subtype='COLOR', size=4, min=0, max=1, default=CURRENT_DEFAULTS['col_thyp'], update=on_property_update)
    
    vis_shyp: BoolProperty(default=CURRENT_DEFAULTS['vis_shyp'], update=on_property_update)
    col_shyp: FloatVectorProperty(name="Spacelike Hyp", subtype='COLOR', size=4, min=0, max=1, default=CURRENT_DEFAULTS['col_shyp'], update=on_property_update)
    
    vis_ctp: BoolProperty(default=CURRENT_DEFAULTS['vis_ctp'], update=on_property_update)
    col_ctp: FloatVectorProperty(name="ct' Axis", subtype='COLOR', size=4, min=0, max=1, default=CURRENT_DEFAULTS['col_ctp'], update=on_property_update)
    
    vis_xp: BoolProperty(default=CURRENT_DEFAULTS['vis_xp'], update=on_property_update)
    col_xp: FloatVectorProperty(name="x' Axis", subtype='COLOR', size=4, min=0, max=1, default=CURRENT_DEFAULTS['col_xp'], update=on_property_update)

# ==============================================================================
#  OPERATORS
# ==============================================================================

class OT_CopyMinkowskiFullCode(Operator):
    bl_idname = f"{OP_PREFIX}.copy_full_script"
    bl_label = "Copy Full Script"
    
    def execute(self, context):
        props = getattr(context.scene, PROPS_NAME)
        target_text = None
        for t in bpy.data.texts:
            if SOURCE_ID_TAG in t.as_string():
                target_text = t
                break
        
        if not target_text:
            self.report({'ERROR'}, "Script source ID not found in Text Editor.")
            return {'CANCELLED'}

        code = target_text.as_string()
        def f4(v): return tuple(round(x, 4) for x in v)
        p = props.precision
        
        new_dict = "CURRENT_DEFAULTS = {\n"
        new_dict += f'    "show_preview": {props.show_preview},\n'
        new_dict += f'    "beta": {props.beta:.{p}f}, "thickness": {props.thickness:.4f},\n'
        new_dict += f'    "precision": {props.precision},\n'
        new_dict += f'    "vis_axis": {props.vis_axis}, "col_axis": {f4(props.col_axis)},\n'
        new_dict += f'    "vis_light": {props.vis_light}, "col_light": {f4(props.col_light)},\n'
        new_dict += f'    "vis_thyp": {props.vis_thyp}, "col_thyp": {f4(props.col_thyp)},\n'
        new_dict += f'    "vis_shyp": {props.vis_shyp}, "col_shyp": {f4(props.col_shyp)},\n'
        new_dict += f'    "vis_ctp": {props.vis_ctp}, "col_ctp": {f4(props.col_ctp)},\n'
        new_dict += f'    "vis_xp": {props.vis_xp}, "col_xp": {f4(props.col_xp)},\n'
        new_dict += "}\n"

        try:
            start_tag = "".join(["#", " <BEGIN_DICT>"])
            end_tag = "".join(["#", " <END_DICT>"])
            
            if start_tag not in code or end_tag not in code:
                self.report({'ERROR'}, "Dictionary tags missing.")
                return {'CANCELLED'}
                
            pre_part = code.split(start_tag, 1)[0]
            post_part = code.split(end_tag, 1)[1]
            
            if pre_part.startswith("# Copied at:"):
                parts = pre_part.split("\n", 1)
                if len(parts) > 1:
                    pre_part = parts[1]
            
            # 年月日を YYYYMMDD HH:MM:SS の形式で付与
            final_code = (
                f"# Copied at: {datetime.now().strftime('%Y%m%d %H:%M:%S')}\n" + 
                pre_part + start_tag + "\n" + 
                new_dict + 
                end_tag + post_part
            )
            
            context.window_manager.clipboard = final_code
            self.report({'INFO'}, "Full script copied to clipboard!")
        except Exception as e:
            self.report({'ERROR'}, f"Copy failed: {str(e)}")
            return {'CANCELLED'}
            
        return {'FINISHED'}

class OT_CopyMinkowskiIntersections(Operator):
    bl_idname = f"{OP_PREFIX}.copy_intersections"
    bl_label = "Copy Intersections"
    
    def execute(self, context):
        props = getattr(context.scene, PROPS_NAME)
        beta = props.beta
        gamma = 1 / math.sqrt(1 - beta**2) if beta < 0.999 else 20.0
        p = props.precision
        
        t_x, t_ct = beta * gamma, gamma
        s_x, s_ct = gamma, beta * gamma
        
        text = (
            f"Beta (v/c): {beta:.{p}f}\n"
            f"Time Intersection (x, ct): ({t_x:.{p}f}, {t_ct:.{p}f})\n"
            f"Space Intersection (x, ct): ({s_x:.{p}f}, {s_ct:.{p}f})"
        )
        context.window_manager.clipboard = text
        self.report({'INFO'}, "Intersections copied to clipboard!")
        return {'FINISHED'}

class OT_OpenMinkowskiUrl(Operator):
    bl_idname = f"{OP_PREFIX}.open_url"; bl_label = "Open URL"; url: StringProperty()
    def execute(self, context): webbrowser.open(self.url); return {'FINISHED'}

class OT_RemoveMinkowskiAddon(Operator):
    bl_idname = f"{OP_PREFIX}.remove_addon"; bl_label = "Remove Addon"
    def execute(self, context):
        bpy.app.timers.register(lambda: unregister(), first_interval=0.1)
        return {'FINISHED'}

# ==============================================================================
#  PANELS
# ==============================================================================

class PT_MinkowskiPanel(Panel):
    bl_label = "Minkowski Stable V7"
    bl_idname = f"{PREFIX}_PT_main"
    bl_space_type = 'VIEW_3D'; bl_region_type = 'UI'; bl_category = TAB_NAME

    def draw(self, context):
        layout = self.layout
        props = getattr(context.scene, PROPS_NAME)
        p = props.precision

        row = layout.row()
        row.scale_y = 1.2
        row.operator(OT_CopyMinkowskiFullCode.bl_idname, icon='COPY_ID', text="Copy Setup as Script")
        
        layout.prop(props, "show_preview", toggle=True, icon='HIDE_OFF' if props.show_preview else 'HIDE_ON')
        
        # 物理パラメータ (Beta表示を指定桁数に連動)
        box = layout.box()
        box.label(text="Physical Parameters", icon='PHYSICS')
        box.prop(props, "beta", slider=True, text=f"Beta (v/c) :  {props.beta:.{p}f}")
        box.prop(props, "thickness", slider=True)

        # 交点情報エリア
        box = layout.box()
        box.label(text="Intersections (x, ct)", icon='MESH_GRID')
        box.prop(props, "precision", slider=True)
        
        beta = props.beta
        gamma = 1 / math.sqrt(1 - beta**2) if beta < 0.999 else 20.0
        
        t_x, t_ct = beta * gamma, gamma
        s_x, s_ct = gamma, beta * gamma
        
        col = box.column(align=True)
        col.label(text=f"Time (ct' axis):  ({t_x:.{p}f}, {t_ct:.{p}f})")
        col.label(text=f"Space (x' axis): ({s_x:.{p}f}, {s_ct:.{p}f})")
        
        row = box.row()
        row.operator(OT_CopyMinkowskiIntersections.bl_idname, icon='COPYDOWN', text="Copy Intersections")

        # 色・表示設定
        box = layout.box()
        box.label(text="Layers & Colors", icon='COLOR')
        
        layers =[
            ("vis_axis", "col_axis", "Main Axes"),
            ("vis_light", "col_light", "Light Cone"),
            ("vis_thyp", "col_thyp", "Timelike Hyp"),
            ("vis_shyp", "col_shyp", "Spacelike Hyp"),
            ("vis_ctp", "col_ctp", "ct' Axis"),
            ("vis_xp", "col_xp", "x' Axis"),
        ]

        for vis, col, label in layers:
            row = box.row(align=True)
            row.prop(props, vis, text="", icon='HIDE_OFF' if getattr(props, vis) else 'HIDE_ON')
            row.prop(props, col, text=label)

class PT_MinkowskiLinks(Panel):
    bl_label = "Links"; bl_idname = f"{PREFIX}_PT_links"; bl_space_type = 'VIEW_3D'; bl_region_type = 'UI'; bl_category = TAB_NAME; bl_options = {'DEFAULT_CLOSED'}
    def draw(self, context):
        for l in ADDON_LINKS: self.layout.operator(OT_OpenMinkowskiUrl.bl_idname, text=l["label"]).url = l["url"]

class PT_MinkowskiSys(Panel):
    bl_label = "System"; bl_idname = f"{PREFIX}_PT_sys"; bl_space_type = 'VIEW_3D'; bl_region_type = 'UI'; bl_category = TAB_NAME; bl_options = {'DEFAULT_CLOSED'}
    def draw(self, context): self.layout.operator(OT_RemoveMinkowskiAddon.bl_idname, icon='CANCEL')

# ==============================================================================
#  REGISTER
# ==============================================================================

classes = (PG_MinkowskiProps, OT_CopyMinkowskiFullCode, OT_CopyMinkowskiIntersections, OT_OpenMinkowskiUrl, OT_RemoveMinkowskiAddon, PT_MinkowskiPanel, PT_MinkowskiLinks, PT_MinkowskiSys)

def register():
    for c in classes: bpy.utils.register_class(c)
    setattr(bpy.types.Scene, PROPS_NAME, PointerProperty(type=PG_MinkowskiProps))

def unregister():
    cleanup_minkowski_data()
    if hasattr(bpy.types.Scene, PROPS_NAME): delattr(bpy.types.Scene, PROPS_NAME)
    for c in reversed(classes): bpy.utils.unregister_class(c)

if __name__ == "__main__": register()