ling-lang 2030.0.5

Ling - The Omniglot Systems Language
// examples/gfx.ling — ling-graphics terminal preview
//
// Renders a Lambert-shaded scene (sphere, icosphere, cube, floor)
// as colored Unicode art, then prints the ling-graphics API reference.
//
//   ling run examples/gfx.ling

// ── Global scene constants ────────────────────────────────────────────────────

// Directional light (pointing toward the scene)
bind LX = -0.45
bind LY = -0.75
bind LZ =  0.55

// Ambient term
bind AMB = 0.15

// Canvas
bind COLS = 56
bind ROWS = 24

// Object centres (canvas pixel coords) and radii
bind S1CX = 28.0   bind S1CY = 12.0   bind S1R = 10.5  // main sphere   (red)
bind S2CX = 46.0   bind S2CY = 12.0   bind S2R =  6.0  // icosphere     (gold)
bind B1CX =  9.0   bind B1CY = 12.0   bind B1W =  5.5  bind B1H = 4.5   // cube (blue)

// Floor starts at row 19
bind FLOOR_ROW = 19.0

// ── Math ─────────────────────────────────────────────────────────────────────

fn clamp(v, lo, hi) {
    if v < lo { return lo }
    if v > hi { return hi }
    return v
}

// Newton-Raphson sqrt via recursion (12 iterations)
fn sqr_iter(x, n, i) {
    if i <= 0 { return x }
    return sqr_iter((x + n / x) * 0.5, n, i - 1)
}

fn sqr(n) {
    if n <= 0.0 { return 0.0 }
    return sqr_iter(n * 0.5 + 0.5, n, 12)
}

// ── Shading ───────────────────────────────────────────────────────────────────

// Map brightness [0,1] → ASCII density char
fn luma(b) {
    if b < 0.07 { return " " }
    if b < 0.17 { return "." }
    if b < 0.30 { return ":" }
    if b < 0.43 { return "-" }
    if b < 0.57 { return "+" }
    if b < 0.70 { return "=" }
    if b < 0.83 { return "#" }
    return "@"
}

// Lambert diffuse + ambient for a unit sphere centred at (0,0).
// px,py in [-1,1].  Returns brightness, or -1 on ray miss.
fn sphere_shade(px, py) {
    bind r2 = px * px + py * py
    if r2 > 1.0 { return -1.0 }
    bind pz  = sqr(1.0 - r2)
    bind len = sqr(LX * LX + LY * LY + LZ * LZ)
    bind lx  = LX / len
    bind ly  = LY / len
    bind lz  = LZ / len
    bind ndl = px * lx + py * ly + pz * lz
    return clamp(AMB + clamp(ndl, 0.0, 1.0) * (1.0 - AMB), 0.0, 1.0)
}

// ── Per-object colored chars (pre-baked ANSI levels) ─────────────────────────

fn red_char(b) {
    bind c = luma(b)
    if b < 0.30 { return "\x1b[31m" + c + "\x1b[0m" }
    if b < 0.60 { return "\x1b[91m" + c + "\x1b[0m" }
    return "\x1b[97m" + c + "\x1b[0m"
}

fn gold_char(b) {
    bind c = luma(b)
    if b < 0.40 { return "\x1b[33m" + c + "\x1b[0m" }
    return "\x1b[93m" + c + "\x1b[0m"
}

fn blue_char(b) {
    bind c = luma(b)
    if b < 0.45 { return "\x1b[34m" + c + "\x1b[0m" }
    return "\x1b[94m" + c + "\x1b[0m"
}

// ── Per-pixel logic ───────────────────────────────────────────────────────────

fn pixel(col, row) {
    // Sphere 1 — red, centre (S1CX, S1CY)
    bind p1x = (col - S1CX) / S1R
    bind p1y = (row - S1CY) / S1R * 2.1
    bind b1  = sphere_shade(p1x, p1y)
    if b1 >= 0.0 { return red_char(b1) }

    // Sphere 2 — gold, centre (S2CX, S2CY)
    bind p2x = (col - S2CX) / S2R
    bind p2y = (row - S2CY) / S2R * 2.1
    bind b2  = sphere_shade(p2x, p2y)
    if b2 >= 0.0 { return gold_char(b2) }

    // Cube — blue, axis-aligned box test
    bind bx = (col - B1CX) / B1W
    bind by = (row - B1CY) / B1H * 2.1
    if bx > -1.0 {
        if bx < 1.0 {
            if by > -1.0 {
                if by < 1.0 {
        bind bd = clamp(AMB + (1.0 - bx * bx - by * by * 0.25) * 0.68, 0.0, 1.0)
        return blue_char(bd)
                }
            }
        }
    }


    // Checkerboard floor
    if row > FLOOR_ROW {
        bind s = (col + row) % 2
        if s < 0.5 { return "\x1b[90m░\x1b[0m" }
        return "\x1b[90m·\x1b[0m"
    }

    // Sky — dim near horizon, empty above
    if row > 16.0 { return "\x1b[90m·\x1b[0m" }
    return " "
}

// ── Recursive row/column builders ─────────────────────────────────────────────

fn build_row(col, row) {
    if col >= COLS { return "" }
    return pixel(col, row) + build_row(col + 1, row)
}

fn render(row) {
    if row >= ROWS { return }
    print("  \x1b[90m│\x1b[0m" + build_row(0, row) + "\x1b[90m│\x1b[0m")
    render(row + 1)
}

// ── Entry point ───────────────────────────────────────────────────────────────

bind start = do {

    print("")
    print("  \x1b[1;96m╔══════════════════════════════════════════════════════════╗\x1b[0m")
    print("  \x1b[1;96m║\x1b[0m  \x1b[1mling-graphics\x1b[0m — 3D/4D Renderer & Scene Graph Engine  \x1b[1;96m║\x1b[0m")
    print("  \x1b[1;96m╠══════════════════════════════════════════════════════════╣\x1b[0m")
    print("  \x1b[1;96m║\x1b[0m  camera : Camera3D::perspective(fov=60, 16:9, z=0.1)    \x1b[1;96m║\x1b[0m")
    print("  \x1b[1;96m║\x1b[0m  light  : directional(-0.45,-0.75, 0.55) + amb=0.15    \x1b[1;96m║\x1b[0m")
    print("  \x1b[1;96m║\x1b[0m  shader : Lambert diffuse · alpha blend · emissive      \x1b[1;96m║\x1b[0m")
    print("  \x1b[1;96m╚══════════════════════════════════════════════════════════╝\x1b[0m")
    print("")
    print("  Scene objects:")
    print("    \x1b[1;31m◉\x1b[0m  sphere(r=1.0)        pos=( 0, 0, 0)   mat=matte_red")
    print("    \x1b[1;33m◉\x1b[0m  icosphere(subs=3)    pos=( 3, 0, 1)   mat=gold_metal")
    print("    \x1b[1;34m■\x1b[0m  cube(half=0.7)       pos=(-3, 0, 1)   mat=crystal_blue")
    print("    \x1b[90m░\x1b[0m  plane(half=10,sub=8) pos=( 0,-1.5, 0) mat=checker_grey")
    print("")
    print("  \x1b[90m┌" + build_row(0, -99) + "┐\x1b[0m")

    // — draw using border chars instead of build_row for the top/bottom bar —
    print("  \x1b[90m┌──────────────────────────────────────────────────────────┐\x1b[0m")
    render(0)
    print("  \x1b[90m└──────────────────────────────────────────────────────────┘\x1b[0m")
    print("")

    // ── API reference ─────────────────────────────────────────────────────────

    print("  \x1b[1mGeometry  (ling_graphics::geometry)\x1b[0m")
    print("    cube(half)    sphere(r, rings, sectors)   icosphere(r, subdivisions)")
    print("    cone(r, h)    pyramid(base, h)             cylinder(r, h, segs)")
    print("    torus(R, r)   plane(half, subdivisions)")
    print("")
    print("  \x1b[1mCamera\x1b[0m")
    print("    Camera3D::perspective(60.0, 16.0/9.0, 0.1, 1000.0)")
    print("    Camera3D::orthographic(5.0, 1.0, 0.1, 100.0)")
    print("    cam.look_at(target, up)   cam.orbit(target, yaw, pitch)")
    print("    cam.unproject_ray(ndc_x, ndc_y)  →  Ray3")
    print("")
    print("  \x1b[1m4-D Hyperbolic Camera\x1b[0m")
    print("    Camera4D::new(HyperModel::Klein)         // geodesics = straight lines")
    print("    Camera4D::new(HyperModel::Poincare)      // angle-preserving")
    print("    Camera4D::new(HyperModel::CrossSection { w_slice: 0.0 })")
    print("    cam4d.move_by(direction, hyperbolic_dist) // Lorentz boost navigation")
    print("")
    print("  \x1b[1mAnimation\x1b[0m")
    print("    Track::new()")
    print("      .add(0.0,  Vec3::ZERO,  EaseFunction::Linear)")
    print("      .add(1.0,  Vec3::Y,     EaseFunction::ElasticOut)")
    print("      .add(2.5,  Vec3::ZERO,  EaseFunction::BounceOut)")
    print("    timeline.tick(dt)   timeline.play()   timeline.stop()")
    print("")
    print("  \x1b[1mFont & text in 3D/4D\x1b[0m")
    print("    bind atlas = FontAtlas::from_bytes(ttf_bytes, 1024)")
    print("    bind mesh  = generate_text_mesh(atlas, \"Hello 4D!\", 48.0, Color::WHITE)")
    print("    scene.spawn_mesh(\"label\", mesh, mat, Transform::from_translation(pos))")
    print("")
    print("  \x1b[1mBlend modes\x1b[0m")
    print("    BlendMode::Normal   BlendMode::Additive   BlendMode::Multiply")
    print("    BlendMode::Screen   BlendMode::Overlay    BlendMode::Subtract")
    print("")
    print("  \x1b[32m✓  ling-graphics v2030.0.0  ·  glam 0.28  ·  fontdue 0.9  ·  pure Rust\x1b[0m")
    print("")
}