Source: primitives/Cylinder.js

/**
 *  @method createCylinder
 *  @memberof SQR.Primitives
 *
 *  @description Creates a cylinder with UVs, non-indexed
 *
 *  @param {Number} height - height of the cylinder
 *  @param {Number} radius - radius of the cylinder
 *  @param {Number} segments - number of segments along the cylinder
 *  @param {Object} options - additional options
 *
 *  @todo document the options
 *
 *  @returns {SQR.Buffer}
 */
SQR.Primitives.createCylinder = function(height, radius, segments, options) {

    options = options || {};

    var topVectors, bottomVectors, topUV, bottomUV;
    var topMiddle, bottomMiddle;

    var faces = [], vertices = [], normals = [], texcoords = [];

    var addFace = function(v1, v2, v3, v4, t1, t2, t3, t4) {
        var f = new SQR.Face().setPosition(v1, v2, v3, v4).setUV(t1, t2, t3, t4);
        faces.push(f);
    }

    topVectors = [];
    bottomVectors = [];
    topUV = [];
    bottomUV = [];

    for(var i = 0; i < segments; i++) {

        var t = new SQR.V3(), b = new SQR.V3();
        var tuv = new SQR.V2(), buv = new SQR.V2();

        var cos = Math.cos(i / segments * SQR.TWOPI) * radius;
        var sin = Math.sin(i / segments * SQR.TWOPI) * radius;

        if(options.vertical) {
            t.set(sin, height * -0.5, cos);
            b.set(sin, height *  0.5, cos);
        } else {
            t.set(height * -0.5, cos, sin);
            b.set(height *  0.5, cos, sin);
        }

        tuv.set(i/segments, 0);
        buv.set(i/segments, 1);

        if(!options.noCaps) {
            if(options.vertical) {
                topMiddle = new SQR.V3(0, height * -0.5, 0);
                bottomMiddle = new SQR.V3(0, height *  0.5, 0);
            } else {
                topMiddle = new SQR.V3(height * -0.5, 0, 0);
                bottomMiddle = new SQR.V3(height *  0.5, 0, 0);
            }
        }

        topVectors.push(t);
        bottomVectors.push(b);

        topUV.push(tuv);
        bottomUV.push(buv);

        if(options.insideFaces) {
            t._inside = t.clone();
            b._inside = b.clone();
        }
    }

    for(var i = 0; i < segments; i++) {
        
        var t0 = topVectors[i];
        var b0 = bottomVectors[i];
        var t0uv = topUV[i];
        var b0uv = bottomUV[i];

        var n = (i + 1) % segments;

        var t1 = topVectors[n];
        var b1 = bottomVectors[n];
        var t1uv = topUV[n];
        var b1uv = bottomUV[n];

        options.heightSegments = options.heightSegments || options.hs || 0;

        if(!options.heightSegments) {

            addFace(t0, b0, t1, b1, t0uv, b0uv, t1uv, b1uv);           

            if(options.noCaps && options.insideFaces) {
                addFace(t0._inside, t1._inside, b0._inside, b1._inside, t0uv, b0uv, t1uv, b1uv);
            }
        } else {
            var t0b0 = new SQR.V3().sub(b0, t0);
            var t1b1 = new SQR.V3().sub(b1, t1);

            var t0l = new SQR.V3().copyFrom(t0);
            var t1l = new SQR.V3().copyFrom(t1);

            var t0c = new SQR.V3();
            var t1c = new SQR.V3();

            var n = options.heightSegments;

            for(var hs = 0; hs < n + 1; hs++) {
                t0c.copyFrom(t0b0).mul(1/n * hs).add(t0, t0c);
                t1c.copyFrom(t1b1).mul(1/n * hs).add(t1, t1c);

                addFace(t0l.clone(), t0c.clone(), t1l.clone(), t1c.clone(), t0uv, b0uv, t1uv, b1uv);  

                t0l.copyFrom(t0c);
                t1l.copyFrom(t1c);
            }
        }

        if(!options.noCaps) {
            addFace(t0, t1, topMiddle, null, b0uv, b1uv, t1uv);
            addFace(b1, b0, bottomMiddle, null, b1uv, b0uv, t1uv);
        }         
    }

    var l = options.layout || {'aPosition': 3, 'aNormal': 3, 'aUV': 2 };

    var geo = SQR.Buffer()
        .layout( l, faces.length * 6);

    var c = 0, t;
    faces.forEach(function(t) {
        c += t.calculateNormal().toBuffer(geo, c);
    });

    return geo;
    
}