klein8_bottle.lua


NAME
    klein8_bottle

FUNCTION
    klein8_bottle(n, a)

NOTES
    Generate Figure-8 Klein bottle surface parametric equations of
    
        x = cos(u)[cos(u/2)(sqrt(2)+cos(v)) + sin(u/2)sin(v)cos(v)]
        y = sin(u)[cos(u/2)(sqrt(2)+cos(v)) + sin(u/2)sin(v)cos(v)]
        z = -sin(u/2)(sqrt(2)+cos(v)) + cos(u/2)sin(v)cos(v)
        
    Refer to MathWorld at http://mathworld.wolfram.com/KleinBottle.html
    
    Example:
        require("plot_simple")
        plot = plot_simple.new()
        plot:add_static(zeGrf.new("light"))
        require("klein8_bottle")
        xyz, nor = klein8_bottle(36, 1)
        xyz:scale(70, 70, 70)
        shape = zeGrf.new("polygon")
        shape:set{type = "quads", vertex = xyz,
                  vertex_normal = nor, color = {0, .7, .7, 1}}
        plot:add(shape)
        plot:animate()
NPUTS
    n - number of u, v-segments
    a - parameter of shape size in x, y direction

OUTPUTS
    Two zeVertex objects containing coordinates and normals.

SOURCE

require("surface_generator")

function klein8_bottle(n, a)
    assert(n > 16)
    assert(a > 0)

    local function cfunc(u, v)
        local cosu, sinu, cos2u, sin2u, sinv, sin2v =
              math.cos(u), math.sin(u),
              math.cos(u/2), math.sin(u/2),
              math.sin(v), math.sin(2*v)
        local r = a + sinv*cos2u - sin2v*sin2u/2
        return cosu*r,
               sinu*r,
               sin2u*sinv + cos2u*sin2v/2
    end

    local function nfunc(u, v)
        local cosu, sinu, cos2u, sin2u,
              cosv, sinv, cos2v, sin2v =
              math.cos(u), math.sin(u), math.cos(u/2), math.sin(u/2),
              math.cos(v), math.sin(v), math.cos(2*v), math.sin(2*v)
        local r = a + sinv*cos2u - sin2v*sin2u/2
        local ru = -sinv*sin2u/2 - sin2v*cos2u/4
        local rv = cosv*cos2u - cos2v*sin2u
        if v < 0 then
            return zeMake.normal2(0, 0, 0,
                  -sinu*r + cosu*ru,
                   cosu*r + sinu*ru,
                   cos2u*sinv/2 - sin2u*sin2v/4,
                   cosu*rv,
                   sinu*rv,
                   sin2u*cosv + cos2u*cos2v)
        else
            return zeMake.normal2(0, 0, 0,
                   cosu*rv,
                   sinu*rv,
                   sin2u*cosv + cos2u*cos2v,
                  -sinu*r + cosu*ru,
                   cosu*r + sinu*ru,
                   cos2u*sinv/2 - sin2u*sin2v/4)
        end
    end

    local U, V = zeUtl.new("double", "double")
    U:range(-math.pi, math.pi/n, 2*n+1)
    V:range(-math.pi, math.pi/n - 1.e-12, n+1)
    
    local xyz = surface_generator(U, V, cfunc)
    local nor = surface_generator(U, V, nfunc)

    V:range(0, math.pi/n, n+1)
    
    local xyz2 = surface_generator(U, V, cfunc)
    local nor2 = surface_generator(U, V, nfunc)
    n = xyz2:size()

    for k = 0, n-1 do
        local x, y, z = xyz2:get(k)
        xyz:add(x, y, z)
        x, y, z = nor2:get(k)
        nor:add(x, y, z)
    end

    return xyz, nor
end