(
    delete objects

    global g_dist = 20.0
    global g_z = 10.0
    global g_size = 8.0
    global g_busy = false

    /* ---------- ヘルパー生成（5x5グリッド） ---------- */
    fn randomColor =
    (
        color (random 0 255) (random 0 255) (random 0 255)
    )

/* ---------- ヘルパー生成（5x5グリッド）---------- */
-- 第1列（固定）
global g_A1 = point name:"A1" pos:[0, g_dist*4, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_B1 = point name:"B1" pos:[0, g_dist*3, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_C1 = point name:"C1" pos:[0, g_dist*2, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_D1 = point name:"D1" pos:[0, g_dist, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_E1 = point name:"E1" pos:[0, 0, g_z] size:g_size wirecolor:(randomColor()) cross:true

-- 第2列
global g_A2 = point name:"A2" pos:[g_dist, g_dist*4, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_B2 = point name:"B2" pos:[g_dist, g_dist*3, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_C2 = point name:"C2" pos:[g_dist, g_dist*2, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_D2 = point name:"D2" pos:[g_dist, g_dist, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_E2 = point name:"E2" pos:[g_dist, 0, g_z] size:g_size wirecolor:(randomColor()) cross:true

-- 第3列
global g_A3 = point name:"A3" pos:[g_dist*2, g_dist*4, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_B3 = point name:"B3" pos:[g_dist*2, g_dist*3, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_C3 = point name:"C3" pos:[g_dist*2, g_dist*2, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_D3 = point name:"D3" pos:[g_dist*2, g_dist, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_E3 = point name:"E3" pos:[g_dist*2, 0, g_z] size:g_size wirecolor:(randomColor()) cross:true

-- 第4列
global g_A4 = point name:"A4" pos:[g_dist*3, g_dist*4, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_B4 = point name:"B4" pos:[g_dist*3, g_dist*3, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_C4 = point name:"C4" pos:[g_dist*3, g_dist*2, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_D4 = point name:"D4" pos:[g_dist*3, g_dist, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_E4 = point name:"E4" pos:[g_dist*3, 0, g_z] size:g_size wirecolor:(randomColor()) cross:true

-- 第5列
global g_A5 = point name:"A5" pos:[g_dist*4, g_dist*4, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_B5 = point name:"B5" pos:[g_dist*4, g_dist*3, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_C5 = point name:"C5" pos:[g_dist*4, g_dist*2, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_D5 = point name:"D5" pos:[g_dist*4, g_dist, g_z] size:g_size wirecolor:(randomColor()) cross:true
global g_E5 = point name:"E5" pos:[g_dist*4, 0, g_z] size:g_size wirecolor:(randomColor()) cross:true

/* ---------- 親行固定 ---------- */
for p in #(g_A1, g_B1, g_C1, g_D1, g_E1) do p.isFrozen = true

/* ---------- ヘルパーのグリッド配列化 ---------- */
global g_grid = #(
    #(g_A1, g_A2, g_A3, g_A4, g_A5),
    #(g_B1, g_B2, g_B3, g_B4, g_B5),
    #(g_C1, g_C2, g_C3, g_C4, g_C5),
    #(g_D1, g_D2, g_D3, g_D4, g_D5),
    #(g_E1, g_E2, g_E3, g_E4, g_E5)
)

/* ---------- ベクトル正規化関数 ---------- */
fn safeNormalize v =
(
    local L = length v
    if L < 1e-4 then [1,0,0] else v / L
)

/* ---------- 平行四辺形維持（Z軸固定） ---------- */
fn maintainParallelogram mover =
(
    if g_busy then return()
    g_busy = true

    try
    (
        local baseVec, parent, dirVec

        -- 各ポイントに対応する親とベースベクトルを設定
        case mover of
        (
            g_A2: (parent = g_A1; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_B2: (parent = g_B1; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_C2: (parent = g_C1; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_D2: (parent = g_D1; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_E2: (parent = g_E1; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])

            g_A3: (parent = g_A2; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_B3: (parent = g_B2; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_C3: (parent = g_C2; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_D3: (parent = g_D2; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_E3: (parent = g_E2; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])

            g_A4: (parent = g_A3; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_B4: (parent = g_B3; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_C4: (parent = g_C3; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_D4: (parent = g_D3; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_E4: (parent = g_E3; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])

            g_A5: (parent = g_A4; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_B5: (parent = g_B4; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_C5: (parent = g_C4; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_D5: (parent = g_D4; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
            g_E5: (parent = g_E4; baseVec = [mover.pos.x - parent.pos.x, mover.pos.y - parent.pos.y, 0])
        )

        if parent != undefined do
        (
            dirVec = safeNormalize(baseVec) * g_dist

            -- 各ポイントに応じた処理
            if mover == g_A2 or mover == g_B2 or mover == g_C2 or mover == g_D2 or mover == g_E2 then
            (
                -- 第2列の場合
                g_A2.pos = [g_A1.pos.x + dirVec.x, g_A1.pos.y + dirVec.y, g_A2.pos.z]
                g_B2.pos = [g_B1.pos.x + dirVec.x, g_B1.pos.y + dirVec.y, g_B2.pos.z]
                g_C2.pos = [g_C1.pos.x + dirVec.x, g_C1.pos.y + dirVec.y, g_C2.pos.z]
                g_D2.pos = [g_D1.pos.x + dirVec.x, g_D1.pos.y + dirVec.y, g_D2.pos.z]
                g_E2.pos = [g_E1.pos.x + dirVec.x, g_E1.pos.y + dirVec.y, g_E2.pos.z]

                -- 第3列以降の位置を更新
                g_A3.pos = [g_A2.pos.x + (g_A3.pos.x - g_A2.pos.x), g_A2.pos.y + (g_A3.pos.y - g_A2.pos.y), g_A3.pos.z]
                g_B3.pos = [g_B2.pos.x + (g_B3.pos.x - g_B2.pos.x), g_B2.pos.y + (g_B3.pos.y - g_B2.pos.y), g_B3.pos.z]
                g_C3.pos = [g_C2.pos.x + (g_C3.pos.x - g_C2.pos.x), g_C2.pos.y + (g_C3.pos.y - g_C2.pos.y), g_C3.pos.z]
                g_D3.pos = [g_D2.pos.x + (g_D3.pos.x - g_D2.pos.x), g_D2.pos.y + (g_D3.pos.y - g_D2.pos.y), g_D3.pos.z]
                g_E3.pos = [g_E2.pos.x + (g_E3.pos.x - g_E2.pos.x), g_E2.pos.y + (g_E3.pos.y - g_E2.pos.y), g_E3.pos.z]

                g_A4.pos = [g_A3.pos.x + (g_A4.pos.x - g_A3.pos.x), g_A3.pos.y + (g_A4.pos.y - g_A3.pos.y), g_A4.pos.z]
                g_B4.pos = [g_B3.pos.x + (g_B4.pos.x - g_B3.pos.x), g_B3.pos.y + (g_B4.pos.y - g_B3.pos.y), g_B4.pos.z]
                g_C4.pos = [g_C3.pos.x + (g_C4.pos.x - g_C3.pos.x), g_C3.pos.y + (g_C4.pos.y - g_C3.pos.y), g_C4.pos.z]
                g_D4.pos = [g_D3.pos.x + (g_D4.pos.x - g_D3.pos.x), g_D3.pos.y + (g_D4.pos.y - g_D3.pos.y), g_D4.pos.z]
                g_E4.pos = [g_E3.pos.x + (g_E4.pos.x - g_E3.pos.x), g_E3.pos.y + (g_E4.pos.y - g_E3.pos.y), g_E4.pos.z]

                g_A5.pos = [g_A4.pos.x + (g_A5.pos.x - g_A4.pos.x), g_A4.pos.y + (g_A5.pos.y - g_A4.pos.y), g_A5.pos.z]
                g_B5.pos = [g_B4.pos.x + (g_B5.pos.x - g_B4.pos.x), g_B4.pos.y + (g_B5.pos.y - g_B4.pos.y), g_B5.pos.z]
                g_C5.pos = [g_C4.pos.x + (g_C5.pos.x - g_C4.pos.x), g_C4.pos.y + (g_C5.pos.y - g_C4.pos.y), g_C5.pos.z]
                g_D5.pos = [g_D4.pos.x + (g_D5.pos.x - g_D4.pos.x), g_D4.pos.y + (g_D5.pos.y - g_D4.pos.y), g_D5.pos.z]
                g_E5.pos = [g_E4.pos.x + (g_E5.pos.x - g_E4.pos.x), g_E4.pos.y + (g_E5.pos.y - g_E4.pos.y), g_E5.pos.z]
            )

            else if mover == g_A3 or mover == g_B3 or mover == g_C3 or mover == g_D3 or mover == g_E3 then
            (
                -- 第3列の場合
                g_A3.pos = [g_A2.pos.x + dirVec.x, g_A2.pos.y + dirVec.y, g_A3.pos.z]
                g_B3.pos = [g_B2.pos.x + dirVec.x, g_B2.pos.y + dirVec.y, g_B3.pos.z]
                g_C3.pos = [g_C2.pos.x + dirVec.x, g_C2.pos.y + dirVec.y, g_C3.pos.z]
                g_D3.pos = [g_D2.pos.x + dirVec.x, g_D2.pos.y + dirVec.y, g_D3.pos.z]
                g_E3.pos = [g_E2.pos.x + dirVec.x, g_E2.pos.y + dirVec.y, g_E3.pos.z]

                -- 第4列以降の位置を更新
                g_A4.pos = [g_A3.pos.x + (g_A4.pos.x - g_A3.pos.x), g_A3.pos.y + (g_A4.pos.y - g_A3.pos.y), g_A4.pos.z]
                g_B4.pos = [g_B3.pos.x + (g_B4.pos.x - g_B3.pos.x), g_B3.pos.y + (g_B4.pos.y - g_B3.pos.y), g_B4.pos.z]
                g_C4.pos = [g_C3.pos.x + (g_C4.pos.x - g_C3.pos.x), g_C3.pos.y + (g_C4.pos.y - g_C3.pos.y), g_C4.pos.z]
                g_D4.pos = [g_D3.pos.x + (g_D4.pos.x - g_D3.pos.x), g_D3.pos.y + (g_D4.pos.y - g_D3.pos.y), g_D4.pos.z]
                g_E4.pos = [g_E3.pos.x + (g_E4.pos.x - g_E3.pos.x), g_E3.pos.y + (g_E4.pos.y - g_E3.pos.y), g_E4.pos.z]

                g_A5.pos = [g_A4.pos.x + (g_A5.pos.x - g_A4.pos.x), g_A4.pos.y + (g_A5.pos.y - g_A4.pos.y), g_A5.pos.z]
                g_B5.pos = [g_B4.pos.x + (g_B5.pos.x - g_B4.pos.x), g_B4.pos.y + (g_B5.pos.y - g_B4.pos.y), g_B5.pos.z]
                g_C5.pos = [g_C4.pos.x + (g_C5.pos.x - g_C4.pos.x), g_C4.pos.y + (g_C5.pos.y - g_C4.pos.y), g_C5.pos.z]
                g_D5.pos = [g_D4.pos.x + (g_D5.pos.x - g_D4.pos.x), g_D4.pos.y + (g_D5.pos.y - g_D4.pos.y), g_D5.pos.z]
                g_E5.pos = [g_E4.pos.x + (g_E5.pos.x - g_E4.pos.x), g_E4.pos.y + (g_E5.pos.y - g_E4.pos.y), g_E5.pos.z]
            )

            else if mover == g_A4 or mover == g_B4 or mover == g_C4 or mover == g_D4 or mover == g_E4 then
            (
                -- 第4列の場合
                g_A4.pos = [g_A3.pos.x + dirVec.x, g_A3.pos.y + dirVec.y, g_A4.pos.z]
                g_B4.pos = [g_B3.pos.x + dirVec.x, g_B3.pos.y + dirVec.y, g_B4.pos.z]
                g_C4.pos = [g_C3.pos.x + dirVec.x, g_C3.pos.y + dirVec.y, g_C4.pos.z]
                g_D4.pos = [g_D3.pos.x + dirVec.x, g_D3.pos.y + dirVec.y, g_D4.pos.z]
                g_E4.pos = [g_E3.pos.x + dirVec.x, g_E3.pos.y + dirVec.y, g_E4.pos.z]

                -- 第5列の位置を更新
                g_A5.pos = [g_A4.pos.x + (g_A5.pos.x - g_A4.pos.x), g_A4.pos.y + (g_A5.pos.y - g_A4.pos.y), g_A5.pos.z]
                g_B5.pos = [g_B4.pos.x + (g_B5.pos.x - g_B4.pos.x), g_B4.pos.y + (g_B5.pos.y - g_B4.pos.y), g_B5.pos.z]
                g_C5.pos = [g_C4.pos.x + (g_C5.pos.x - g_C4.pos.x), g_C4.pos.y + (g_C5.pos.y - g_C4.pos.y), g_C5.pos.z]
                g_D5.pos = [g_D4.pos.x + (g_D5.pos.x - g_D4.pos.x), g_D4.pos.y + (g_D5.pos.y - g_D4.pos.y), g_D5.pos.z]
                g_E5.pos = [g_E4.pos.x + (g_E5.pos.x - g_E4.pos.x), g_E4.pos.y + (g_E5.pos.y - g_E4.pos.y), g_E5.pos.z]
            )

            else if mover == g_A5 or mover == g_B5 or mover == g_C5 or mover == g_D5 or mover == g_E5 then
            (
                -- 第5列の場合
                g_A5.pos = [g_A4.pos.x + dirVec.x, g_A4.pos.y + dirVec.y, g_A5.pos.z]
                g_B5.pos = [g_B4.pos.x + dirVec.x, g_B4.pos.y + dirVec.y, g_B5.pos.z]
                g_C5.pos = [g_C4.pos.x + dirVec.x, g_C4.pos.y + dirVec.y, g_C5.pos.z]
                g_D5.pos = [g_D4.pos.x + dirVec.x, g_D4.pos.y + dirVec.y, g_D5.pos.z]
                g_E5.pos = [g_E4.pos.x + dirVec.x, g_E4.pos.y + dirVec.y, g_E5.pos.z]
            )
        )
    )
    catch
    (
        format "Error in maintainParallelogram: %\n" (getCurrentException())
    )
    finally
    (
        g_busy = false
    )
)

/* ---------- 統合制御 ---------- */
fn maintainAllConstraints mover =
(
    maintainParallelogram mover
    adjustNeighbors mover
)

/* ---------- ハンドラ登録 ---------- */
deleteAllChangeHandlers id:#NeighborConstraintHandler

when transform g_A2 changes id:#NeighborConstraintHandler do maintainAllConstraints g_A2
when transform g_B2 changes id:#NeighborConstraintHandler do maintainAllConstraints g_B2
when transform g_C2 changes id:#NeighborConstraintHandler do maintainAllConstraints g_C2
when transform g_D2 changes id:#NeighborConstraintHandler do maintainAllConstraints g_D2
when transform g_E2 changes id:#NeighborConstraintHandler do maintainAllConstraints g_E2

when transform g_A3 changes id:#NeighborConstraintHandler do maintainAllConstraints g_A3
when transform g_B3 changes id:#NeighborConstraintHandler do maintainAllConstraints g_B3
when transform g_C3 changes id:#NeighborConstraintHandler do maintainAllConstraints g_C3
when transform g_D3 changes id:#NeighborConstraintHandler do maintainAllConstraints g_D3
when transform g_E3 changes id:#NeighborConstraintHandler do maintainAllConstraints g_E3

when transform g_A4 changes id:#NeighborConstraintHandler do maintainAllConstraints g_A4
when transform g_B4 changes id:#NeighborConstraintHandler do maintainAllConstraints g_B4
when transform g_C4 changes id:#NeighborConstraintHandler do maintainAllConstraints g_C4
when transform g_D4 changes id:#NeighborConstraintHandler do maintainAllConstraints g_D4
when transform g_E4 changes id:#NeighborConstraintHandler do maintainAllConstraints g_E4

when transform g_A5 changes id:#NeighborConstraintHandler do maintainAllConstraints g_A5
when transform g_B5 changes id:#NeighborConstraintHandler do maintainAllConstraints g_B5
when transform g_C5 changes id:#NeighborConstraintHandler do maintainAllConstraints g_C5
when transform g_D5 changes id:#NeighborConstraintHandler do maintainAllConstraints g_D5
when transform g_E5 changes id:#NeighborConstraintHandler do maintainAllConstraints g_E5

format "Setup complete: 全ての列の距離を常時維持する制約システムが有効です。\n"
)
when構文をforループで簡潔にして、ただしMAXSCRIPTではwhen transformで外部のローカル変数参照はできない