/*
    Virtual Bezier Helper Placement (Real-time) - 最終修正版 v5

    機能：
    - splineShapeオブジェクトを一切使わず、4つの制御ヘルパーだけで仮想的な3次ベジェ曲線を計算します。
    - 曲線上に指定された数のヘルパーを「カーブ長で等間隔」に配置します。
    - 制御点を動かすと、配置されたヘルパーがリアルタイムで更新されます。
    - 最後にクリーンアップ機能で、作成したオブジェクトとイベントハンドラをすべて削除します。
*/

try(destroyDialog rlt_virtualBezier)catch()
rollout rlt_virtualBezier "仮想ベジェヘルパー（リアルタイム）"
(
    -- ローカル変数
    local p1, t1, t2, p2 -- 制御用ヘルパー (p1:始点, p2:終点, t1,t2:タンジェント)
    local placementHelpers = #() -- 配置されるヘルパーの配列

    -- //////////////////////////////////////////////////////////////
    -- 3次ベジェ曲線の座標を計算する関数
    -- //////////////////////////////////////////////////////////////
    fn getBezierPoint p1_pos t1_pos t2_pos p2_pos t =
    (
        local it = 1.0 - t
        (it^3 * p1_pos) + (3 * it^2 * t * t1_pos) + (3 * it * t^2 * t2_pos) + (t^3 * p2_pos)
    )

    -- //////////////////////////////////////////////////////////////
    -- ベジェ曲線の全長を計算する関数（近似計算）
    -- //////////////////////////////////////////////////////////////
    fn getBezierLength p1_pos t1_pos t2_pos p2_pos segments:50 =
    (
        local len = 0.0
        local lastP = p1_pos
        for i = 1 to segments do
        (
            local t = i as float / segments
            local p = getBezierPoint p1_pos t1_pos t2_pos p2_pos t
            len += distance p lastP
            lastP = p
        )
        len
    )

    -- //////////////////////////////////////////////////////////////
    -- 指定された「長さ」の位置に相当するパラメータ「t」を見つける関数
    -- //////////////////////////////////////////////////////////////
    fn getTforLength p1_pos t1_pos t2_pos p2_pos targetLength segments:50 =
    (
        if targetLength <= 0.0 then return 0.0

        local len = 0.0
        local lastP = p1_pos
        for i = 1 to segments do
        (
            local t = i as float / segments
            local p = getBezierPoint p1_pos t1_pos t2_pos p2_pos t
            local d = distance p lastP
            if (len + d) >= targetLength do
            (
                local over = targetLength - len
                if d == 0.0 then return t
                return ((i - 1) as float + (over / d)) / segments
            )
            len += d
            lastP = p
        )
        1.0
    )

    -- //////////////////////////////////////////////////////////////
    -- 配置ヘルパーを更新するメイン関数
    -- //////////////////////////////////////////////////////////////
    fn updatePlacement =
    (
        if not isvalidnode p1 or not isvalidnode p2 then return false

        local p1_pos = p1.pos
        local p2_pos = p2.pos
        local t1_pos = t1.pos
        local t2_pos = t2.pos
        
        local totalLength = getBezierLength p1_pos t1_pos t2_pos p2_pos
        if placementHelpers.count == 0 then return ok
        local numSegments = placementHelpers.count + 1
        local segmentLength = totalLength / numSegments

        for i = 1 to placementHelpers.count do
        (
            if not isvalidnode placementHelpers[i] then continue
            local targetLength = segmentLength * i
            local t = getTforLength p1_pos t1_pos t2_pos p2_pos targetLength
            placementHelpers[i].pos = getBezierPoint p1_pos t1_pos t2_pos p2_pos t
        )
        ok
    )
    
    -- //////////////////////////////////////////////////////////////
    -- 配置ヘルパーを（再）作成する関数
    -- //////////////////////////////////////////////////////////////
    fn createPlacementHelpers numToCreate =
    (
        for h in placementHelpers where isvalidnode h do delete h
        placementHelpers = #()
        
        for i = 1 to numToCreate do
        (
            local h = Point name:(uniquename "PlacedHelper_") size:5 wirecolor:blue centermarker:false axistripod:false
            append placementHelpers h
        )
        updatePlacement()
    )
    
    -- //////////////////////////////////////////////////////////////
    -- クリーンアップ関数
    -- //////////////////////////////////////////////////////////////
    fn cleanupScene =
    (
        if isChangeHandlerActive id:#virtual_bezier_update do deleteChangeHandler #virtual_bezier_update
        
        for h in placementHelpers where isvalidnode h do delete h
        for h in #(p1,t1,t2,p2) where isvalidnode h do delete h
        placementHelpers = #()
        p1 = p2 = t1 = t2 = undefined
        
        if isvalidnode btn_placeAndUpdate then btn_placeAndUpdate.enabled = false
        format "更新を停止し、すべてのヘルパーを削除しました。\n"
    )

    -- UI要素
    group "1. 作成" (
        button btn_createControls "1. 制御ヘルパーを作成" width:180 tooltip:"4つの制御用ヘルパーをシーンに作成します"
    )
    group "2. 配置＆更新" (
        spinner spn_numHelpers "配置数:" range:[1,100,9] type:#integer
        button btn_placeAndUpdate "2. ヘルパーを配置＆更新開始" width:180 enabled:false
    )
    group "3. 削除" (
         button btn_cleanup "更新停止＆全ヘルパー削除" width:180 tooltip:"作成した全てのオブジェクトと更新処理を削除します"
    )

    -- UIイベント
    on btn_createControls pressed do
    (
        cleanupScene()
        
        p1 = Point name:(uniquename "Bezier_Start_") pos:[-50,0,0] box:true size:10 wirecolor:green
        p2 = Point name:(uniquename "Bezier_End_") pos:[50,0,0] box:true size:10 wirecolor:red
        t1 = Point name:(uniquename "Bezier_Tangent1_") pos:[-50,50,0] size:8 wirecolor:yellow
        t2 = Point name:(uniquename "Bezier_Tangent2_") pos:[50,50,0] size:8 wirecolor:yellow
        
        for h in #(p1,p2,t1,t2) do (h.centermarker = h.axistripod = false)
        
        btn_placeAndUpdate.enabled = true
        format "制御ヘルパーを作成しました。\n"
    )
    
    on btn_placeAndUpdate pressed do
    (
        if isChangeHandlerActive id:#virtual_bezier_update do deleteChangeHandler #virtual_bezier_update

        createPlacementHelpers spn_numHelpers.value

        -- ★★★ 最終修正点 ★★★
        -- handleAt:#redrawViews を 正しいキーワード #viewRedraw に修正しました。
        local objects_to_watch = #(p1, p2, t1, t2)
        when objects_to_watch transform changes id:#virtual_bezier_update handleAt:#viewRedraw do updatePlacement()

        format "%個のヘルパーを配置し、リアルタイム更新を開始しました。\n" spn_numHelpers.value
    )
    
    on spn_numHelpers changed val do
    (
        if isChangeHandlerActive id:#virtual_bezier_update do createPlacementHelpers val
    )

    on btn_cleanup pressed do
    (
        cleanupScene()
    )

    on rlt_virtualBezier close do
    (
        cleanupScene()
    )
)

-- ダイアログを作成
createDialog rlt_virtualBezier 210 200
これをデバッグして