: AyaanScript+ — SOLID 3D Cube with shaded faces.
: Uses gui_fill_triangle for filled faces with z-sorted painter's algorithm.
: Gradient sky background, big title text, FPS counter.
: ESC quits.

const W = 800
const H = 800
gui_native "AyaanScript+ — Solid 3D Cube" W H
gui_fps 60

func project x y z
    set sx_v = {sin(ax)}
    set cx_v = {cos(ax)}
    set sy_v = {sin(ay)}
    set cy_v = {cos(ay)}
    set sz_v = {sin(az)}
    set cz_v = {cos(az)}
    set y1 = {y * cx_v - z * sx_v}
    set z1 = {y * sx_v + z * cx_v}
    set x1 = x
    set x2 = {x1 * cy_v + z1 * sy_v}
    set z2 = {0 - x1 * sy_v + z1 * cy_v}
    set y2 = y1
    set x3 = {x2 * cz_v - y2 * sz_v}
    set y3 = {x2 * sz_v + y2 * cz_v}
    set z3 = z2
    set pz = {z3 + distance}
    set proj_sx = {cx + (x3 * focal) / pz}
    set proj_sy = {cy + (y3 * focal) / pz}
    set proj_depth = {z3}
endfunc

set cx = 400
set cy = 400
set focal = 700
set distance = 5
set ax = 0
set ay = 0
set az = 0
set running = 1
set frame = 0
set tstart = {time()}
set fps = 0
set shot_taken = 0

while running == 1
    gui_open running
    if running == 0
        break
    endif

    : sky gradient background
    gui_gradient_rect 0 0 W H "#1a0a2e" "#0f0c29"

    : project the 8 cube vertices, scaled
    set s = 1.5
    call project {0-s} {0-s} {0-s}
    set v0x = {proj_sx}
    set v0y = {proj_sy}
    set v0z = {proj_depth}
    call project {s} {0-s} {0-s}
    set v1x = {proj_sx}
    set v1y = {proj_sy}
    set v1z = {proj_depth}
    call project {s} {s} {0-s}
    set v2x = {proj_sx}
    set v2y = {proj_sy}
    set v2z = {proj_depth}
    call project {0-s} {s} {0-s}
    set v3x = {proj_sx}
    set v3y = {proj_sy}
    set v3z = {proj_depth}
    call project {0-s} {0-s} {s}
    set v4x = {proj_sx}
    set v4y = {proj_sy}
    set v4z = {proj_depth}
    call project {s} {0-s} {s}
    set v5x = {proj_sx}
    set v5y = {proj_sy}
    set v5z = {proj_depth}
    call project {s} {s} {s}
    set v6x = {proj_sx}
    set v6y = {proj_sy}
    set v6z = {proj_depth}
    call project {0-s} {s} {s}
    set v7x = {proj_sx}
    set v7y = {proj_sy}
    set v7z = {proj_depth}

    : 6 faces. Each face has 4 verts; average z determines draw order.
    : front 4567, back 0123, left 0374, right 1265, top 0451, bottom 3762
    set zf_front  = {(v4z + v5z + v6z + v7z) / 4}
    set zf_back   = {(v0z + v1z + v2z + v3z) / 4}
    set zf_left   = {(v0z + v3z + v7z + v4z) / 4}
    set zf_right  = {(v1z + v2z + v6z + v5z) / 4}
    set zf_top    = {(v0z + v4z + v5z + v1z) / 4}
    set zf_bottom = {(v3z + v7z + v6z + v2z) / 4}

    : painter's: draw faces from farthest (largest +z) to nearest (smallest)
    : naive sort — 6 faces, just compare and draw in reverse.
    : I encode by drawing all 6 then trusting they paint correctly is wrong;
    : instead loop 6 times finding max-z each pass, then draw.

    set drew_front = 0
    set drew_back = 0
    set drew_left = 0
    set drew_right = 0
    set drew_top = 0
    set drew_bottom = 0
    set pass = 0
    while pass < 6
        : find max z among remaining
        set best = -99999
        set bestname = ""
        if drew_front == 0
            if zf_front > best
                set best = zf_front
                set bestname = "front"
            endif
        endif
        if drew_back == 0
            if zf_back > best
                set best = zf_back
                set bestname = "back"
            endif
        endif
        if drew_left == 0
            if zf_left > best
                set best = zf_left
                set bestname = "left"
            endif
        endif
        if drew_right == 0
            if zf_right > best
                set best = zf_right
                set bestname = "right"
            endif
        endif
        if drew_top == 0
            if zf_top > best
                set best = zf_top
                set bestname = "top"
            endif
        endif
        if drew_bottom == 0
            if zf_bottom > best
                set best = zf_bottom
                set bestname = "bottom"
            endif
        endif

        if bestname == "front"
            gui_fill_triangle v4x v4y v5x v5y v6x v6y "#ff6b6b"
            gui_fill_triangle v4x v4y v6x v6y v7x v7y "#ff6b6b"
            set drew_front = 1
        endif
        if bestname == "back"
            gui_fill_triangle v0x v0y v1x v1y v2x v2y "#4ecdc4"
            gui_fill_triangle v0x v0y v2x v2y v3x v3y "#4ecdc4"
            set drew_back = 1
        endif
        if bestname == "left"
            gui_fill_triangle v0x v0y v3x v3y v7x v7y "#a8e6cf"
            gui_fill_triangle v0x v0y v7x v7y v4x v4y "#a8e6cf"
            set drew_left = 1
        endif
        if bestname == "right"
            gui_fill_triangle v1x v1y v2x v2y v6x v6y "#ffd93d"
            gui_fill_triangle v1x v1y v6x v6y v5x v5y "#ffd93d"
            set drew_right = 1
        endif
        if bestname == "top"
            gui_fill_triangle v0x v0y v4x v4y v5x v5y "#c471f5"
            gui_fill_triangle v0x v0y v5x v5y v1x v1y "#c471f5"
            set drew_top = 1
        endif
        if bestname == "bottom"
            gui_fill_triangle v3x v3y v7x v7y v6x v6y "#56ccf2"
            gui_fill_triangle v3x v3y v6x v6y v2x v2y "#56ccf2"
            set drew_bottom = 1
        endif

        set pass = {pass + 1}
    endwhile

    : edges over the filled faces
    gui_thick_line v0x v0y v1x v1y 2 "white"
    gui_thick_line v1x v1y v2x v2y 2 "white"
    gui_thick_line v2x v2y v3x v3y 2 "white"
    gui_thick_line v3x v3y v0x v0y 2 "white"
    gui_thick_line v4x v4y v5x v5y 2 "white"
    gui_thick_line v5x v5y v6x v6y 2 "white"
    gui_thick_line v6x v6y v7x v7y 2 "white"
    gui_thick_line v7x v7y v4x v4y 2 "white"
    gui_thick_line v0x v0y v4x v4y 2 "white"
    gui_thick_line v1x v1y v5x v5y 2 "white"
    gui_thick_line v2x v2y v6x v6y 2 "white"
    gui_thick_line v3x v3y v7x v7y 2 "white"

    : HUD
    gui_text_big 20 20 "AYAANSCRIPT+ 3D" "white" 2
    gui_text 20 60 "ESC quits  |  ARROWS rotate  |  S screenshot" "lightgray"
    gui_text 20 75 "fps:" "lightgray"
    gui_text 60 75 fps "yellow"

    : input
    gui_key_down "esc" -> esc
    if esc == 1
        set running = 0
    endif
    gui_key_down "left" -> kl
    gui_key_down "right" -> kr
    gui_key_down "up" -> ku
    gui_key_down "down" -> kd
    gui_key_down "space" -> ks
    if ks == 1
        if shot_taken == 0
            gui_screenshot "cube.ppm"
            set shot_taken = 1
        endif
    else
        set shot_taken = 0
    endif

    set ay = {ay + 0.012}
    set ax = {ax + 0.008}
    set az = {az + 0.004}
    if kl == 1
        set ay = {ay - 0.05}
    endif
    if kr == 1
        set ay = {ay + 0.05}
    endif
    if ku == 1
        set ax = {ax - 0.05}
    endif
    if kd == 1
        set ax = {ax + 0.05}
    endif

    set frame = {frame + 1}
    set now = {time()}
    set elapsed = {now - tstart}
    if elapsed > 0
        set fps = {frame / elapsed}
    endif

    gui_update
endwhile

gui_close
psyska -/ "Window closed."
