LatheBufferGeometry
438090
function ( points, segments, phiStart, phiLength ) {

	BufferGeometry.call( this );

	this.type = '';

	this.parameters = {
		points: points,
		segments: segments,
		phiStart: phiStart,
		phiLength: phiLength
	};

	segments = Math.floor( segments ) || 12;
	phiStart = phiStart || 0;
	phiLength = phiLength || Math.PI * 2;

	// clamp phiLength so it's in range of [ 0, 2PI ]

	phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 );


	// buffers

	var indices = [];
	var vertices = [];
	var uvs = [];

	// helper variables

	var base;
	var inverseSegments = 1.0 / segments;
	var vertex = new Vector3();
	var uv = new Vector2();
	var i, j;

	// generate vertices and uvs

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

		var phi = phiStart + i * inverseSegments * phiLength;

		var sin = Math.sin( phi );
		var cos = Math.cos( phi );

		for ( j = 0; j <= ( points.length - 1 ); j ++ ) {

			// vertex

			vertex.x = points[ j ].x * sin;
			vertex.y = points[ j ].y;
			vertex.z = points[ j ].x * cos;

			vertices.push( vertex.x, vertex.y, vertex.z );

			// uv

			uv.x = i / segments;
			uv.y = j / ( points.length - 1 );

			uvs.push( uv.x, uv.y );


		}

	}

	// indices

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

		for ( j = 0; j < ( points.length - 1 ); j ++ ) {

			base = j + i * points.length;

			var a = base;
			var b = base + points.length;
			var c = base + points.length + 1;
			var d = base + 1;

			// faces

			indices.push( a, b, d );
			indices.push( b, c, d );

		}

	}

	// build geometry

	this.setIndex( indices );
	this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
	this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );

	// generate normals

	this.computeVertexNormals();

	// if the geometry is closed, we need to average the normals along the seam.
	// because the corresponding vertices are identical (but still have different UVs).

	if ( phiLength === Math.PI * 2 ) {

		var normals = this.attributes.normal.array;
		var n1 = new Vector3();
		var n2 = new Vector3();
		var n = new Vector3();

		// this is the buffer offset for the last line of vertices

		base = segments * points.length * 3;

		for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) {

			// select the normal of the vertex in the first line

			n1.x = normals[ j + 0 ];
			n1.y = normals[ j + 1 ];
			n1.z = normals[ j + 2 ];

			// select the normal of the vertex in the last line

			n2.x = normals[ base + j + 0 ];
			n2.y = normals[ base + j + 1 ];
			n2.z = normals[ base + j + 2 ];

			// average normals

			n.addVectors( n1, n2 ).normalize();

			// assign the new values to both normals

			normals[ j + 0 ] = normals[ base + j + 0 ] = n.x;
			normals[ j + 1 ] = normals[ base + j + 1 ] = n.y;
			normals[ j + 2 ] = normals[ base + j + 2 ] = n.z;

		}

	}

}
1236516
private static RectBounds fillBuffers(
            PlyReader plyReader,
            FloatBuffer vertexBuffer,
            IntBuffer indexBuffer) throws IOException {

        // Get all element readers and find the two providing 
        // the vertices and triangles.
        ElementReader reader = plyReader.nextElementReader();
        RectBounds bounds = null;
        while (reader != null) {

            if (reader.getElementType().getName().equals("vertex")) {
                bounds = fillVertexBuffer(reader, vertexBuffer);
            } else if (reader.getElementType().getName().equals("face")) {
                fillIndexBuffer(reader, indexBuffer);
            }
            reader.close();
            reader = plyReader.nextElementReader();
        }
        return bounds;
    }
767713
public void writeGeometry(Geometry g) throws IOException {
        if(type == null) {
            throw new IllegalStateException("Header must be written before writeGeometry");
        }
        if(bounds != null) {
            if(g != null) {
                bounds.expandToInclude(g.getEnvelopeInternal());
            }
        } else {
            bounds = g.getEnvelopeInternal();
        }
		int length;
		if (g == null) {
			length = 4;
		} else {
			length = handler.getLength(g);
		}

		length /= 2;

		shapeBuffer.order(ByteOrder.BIG_ENDIAN);
		shapeBuffer.putInt(++cnt);
		shapeBuffer.putInt(length);
		shapeBuffer.order(ByteOrder.LITTLE_ENDIAN);
		if (g == null) {
			shapeBuffer.putInt(0);
		} else {
			shapeBuffer.putInt(type.id);
			handler.write(shapeBuffer, g);
		}

		// write to the shx
		indexBuffer.putInt(offset);
		indexBuffer.putInt(length);
		offset += length + 4;
	}
147824
function ( radius, detail ) {

	var vertices = [
		1, 1, 1, 	- 1, - 1, 1, 	- 1, 1, - 1, 	1, - 1, - 1
	];

	var indices = [
		2, 1, 0, 	0, 3, 2,	1, 3, 0,	2, 3, 1
	];

	PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );

	this.type = '';

	this.parameters = {
		radius: radius,
		detail: detail
	};

}
1122629
function sineGenerator(frequency, sampleRate, blockSize, callback, nbrBlocks = Infinity) {
  const self = {};

  const block = new Float32Array(blockSize);
  const tIncr = frameSize / sampleRate;
  const _2PI = 2 * Math.PI;

  let pIncr = frequency / sampleRate;
  let phase = 0;
  let timeoutId = null;
  let time = 0;
  let counter = 0;

  self.frequency = (frequency) => {
    // mult = _2PI * frequency;
    pIncr = frequency / sampleRate;
  }

  self.start = () => {
    (function createBlock() {
      for (let i = 0; i < blockSize; i++) {
        const value = Math.sin(phase * _2PI);
        block[i] = value;
        phase = (phase + pIncr) % 1;
      }

      callback(time, block);

      time += tIncr;
      counter += 1;

      if (counter < nbrBlocks) {
        timeoutId = setTimeout(createBlock, tIncr * 1000);
        // timeoutId = setTimeout(createBlock, 2000);
      }
    }());
  }

  self.stop = () => {
    clearTimeout(timeoutId);
  }

  return self;
}
449505
protected void drawMeter(Graphics g, int newTop, int newLeft, int newWidth, int newHeight, boolean doClear)
	{
		if (g!=null)
		{
			switch (drawWhat)
			{
				default:
				case DRAW_SA_METER: drawSAMeter(g, newTop, newLeft, newWidth, newHeight, doClear); break; 
				case DRAW_WAVE_METER: drawWaveMeter(g, newTop, newLeft, newWidth, newHeight, doClear); break; 
				case DRAW_SK_METER: drawSKMeter(g, newTop, newLeft, newWidth, newHeight); break; 
			}
		}
		for (int i=0; i<bands; i++)
		{
			fftLevels[i]-=rampDownValue;
			if (fftLevels[i]<0.0F) fftLevels[i]=0.0F;
			
			maxFFTLevels[i] -= maxPeakLevelRampDownValue[i];
			if (maxFFTLevels[i]<0.0F) 
				maxFFTLevels[i]=0.0F;
			else
				maxPeakLevelRampDownValue[i] += maxPeakLevelRampDownDelay;
		}
	}
435292
def pt_on_bezier_curve(P=[(0.0, 0.0)], t=0.5):
    '''
    '''
    assert isinstance(P, list)
    assert len(P) > 0
    for p in P:
        assert isinstance(p, tuple)
        for i in p:
            assert len(p) > 1
            assert isinstance(i, float)
    assert isinstance(t, float)
    assert 0 <= t <= 1
    
    O = len(P) - 1 # Order of curve

    # Recurse down the orders calculating the next set of control points until
    #   there is only one left, which is the point we want.
    Q = P
    while O > 0:
        Q = [pt_between_pts(Q[l], Q[l+1], t) for l in range(O)]
        O -= 1
    
    assert len(Q) == 1
    return Q[0]
160039
function Slide(width, height, animationPhase) {
  // create a geometry that will be used by BAS.ModelBufferGeometry
  // its a plane with a bunch of segments
  var plane = new THREE.PlaneGeometry(width, height, width * 2, height * 2);

  // duplicate some vertices so that each face becomes a separate triangle.
  // this is the same as the THREE.ExplodeModifier
  BAS.Utils.separateFaces(plane);

  // create a ModelBufferGeometry based on the geometry created above
  // ModelBufferGeometry makes it easier to create animations based on faces of a geometry
  // it is similar to the PrefabBufferGeometry where the prefab is a face (triangle)
  var geometry = new BAS.ModelBufferGeometry(plane, {
    // setting this to true will store the vertex positions relative to the face they are in
    // this way it's easier to rotate and scale faces around their own center
    localizeFaces: true,
    // setting this to true will store a centroid for each face in an array
    computeCentroids: true
  });

  // buffer UVs so the textures are mapped correctly
  geometry.bufferUvs();

  var i, j, offset, centroid;

  // ANIMATION

  var aDelayDuration = geometry.createAttribute('aDelayDuration', 2);
  // these will be used to calculate the animation delay and duration for each face
  var minDuration = 0.8;
  var maxDuration = 1.2;
  var maxDelayX = 0.9;
  var maxDelayY = 0.125;
  var stretch = 0.11;

  this.totalDuration = maxDuration + maxDelayX + maxDelayY + stretch;

  for (i = 0, offset = 0; i < geometry.faceCount; i++) {
    centroid = geometry.centroids[i];

    var duration = THREE.Math.randFloat(minDuration, maxDuration);
    // delay is based on the position of each face within the original plane geometry
    // because the faces are localized, this position is available in the centroids array
    var delayX = THREE.Math.mapLinear(centroid.x, -width * 0.5, width * 0.5, 0.0, maxDelayX);
    var delayY;

    // create a different delayY mapping based on the animation phase (in or out)
    if (animationPhase === 'in') {
      delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, 0.0, maxDelayY)
    }
    else {
      delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, maxDelayY, 0.0)
    }

    // store the delay and duration FOR EACH VERTEX of the face
    for (j = 0; j < 3; j++) {
      // by giving each VERTEX a different delay value the face will be 'stretched' in time
      aDelayDuration.array[offset]     = delayX + delayY + (Math.random() * stretch * duration);
      aDelayDuration.array[offset + 1] = duration;

      offset += 2;
    }
  }

  // POSITIONS

  // the transitions will begin and end on the same position
  var aStartPosition = geometry.createAttribute('aStartPosition', 3, function(data, i) {
    geometry.centroids[i].toArray(data);
  });
  var aEndPosition = geometry.createAttribute('aEndPosition', 3, function(data, i) {
    geometry.centroids[i].toArray(data);
  });

  // CONTROL POINTS

  // each face will follow a bezier path
  // since all paths begin and end on the position (the centroid), the control points will determine how the animation looks
  var aControl0 = geometry.createAttribute('aControl0', 3);
  var aControl1 = geometry.createAttribute('aControl1', 3);

  var control0 = new THREE.Vector3();
  var control1 = new THREE.Vector3();
  var data = [];

  for (i = 0, offset = 0; i < geometry.faceCount; i++) {
    centroid = geometry.centroids[i];

    // the logic to determine the control points is completely arbitrary
    var signY = Math.sign(centroid.y);

    control0.x = THREE.Math.randFloat(0.1, 0.3) * 50;
    control0.y = signY * THREE.Math.randFloat(0.1, 0.3) * 70;
    control0.z = THREE.Math.randFloatSpread(20);

    control1.x = THREE.Math.randFloat(0.3, 0.6) * 50;
    control1.y = -signY * THREE.Math.randFloat(0.3, 0.6) * 70;
    control1.z = THREE.Math.randFloatSpread(20);

    if (animationPhase === 'in') {
      control0.subVectors(centroid, control0);
      control1.subVectors(centroid, control1);
    }
    else { // out
      control0.addVectors(centroid, control0);
      control1.addVectors(centroid, control1);
    }

    // store the control points per face
    // this is similar to THREE.PrefabBufferGeometry.setPrefabData
    geometry.setFaceData(aControl0, i, control0.toArray(data));
    geometry.setFaceData(aControl1, i, control1.toArray(data));
  }

  var texture = new THREE.Texture();
  texture.minFilter = THREE.NearestFilter;

  var material = new BAS.BasicAnimationMaterial({
    flatShading: true,
    side: THREE.DoubleSide,
    uniforms: {
      uTime: {value: 0}
    },
    uniformValues: {
      map: texture
    },
    vertexFunctions: [
      BAS.ShaderChunk['cubic_bezier'],
      BAS.ShaderChunk['ease_cubic_in_out'],
      BAS.ShaderChunk['quaternion_rotation']
    ],
    vertexParameters: [
      'uniform float uTime;',
      'attribute vec2 aDelayDuration;',
      'attribute vec3 aStartPosition;',
      'attribute vec3 aControl0;',
      'attribute vec3 aControl1;',
      'attribute vec3 aEndPosition;'
    ],
    vertexInit: [
      'float tProgress = clamp(uTime - aDelayDuration.x, 0.0, aDelayDuration.y) / aDelayDuration.y;'
    ],
    vertexPosition: [
      // this scales each face
      // for the in animation, we want to go from 0.0 to 1.0
      // for the out animation, we want to go from 1.0 to 0.0
      (animationPhase === 'in' ? 'transformed *= tProgress;' : 'transformed *= 1.0 - tProgress;'),
      // translation based on the bezier curve defined by the attributes
      'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);'
    ]
  });

  THREE.Mesh.call(this, geometry, material);

  this.frustumCulled = false;
}
1191470
function Animation(prefabCount, prefabSize, bounds) {
  this.bounds = bounds;

  // create a prefab
  var prefab = new THREE.PlaneGeometry(prefabSize, prefabSize, 1, 8);

  // create a geometry where the prefab is repeated 'prefabCount' times
  var geometry = new NuggetCollisionGeometry(prefab, prefabCount);
  
  // animation timing

  // each prefab has a start time (delay) and duration
  var aDelayDuration = geometry.createAttribute('aDelayDuration', 2);
  var delay;
  var duration;
  var minDuration = 0.25;
  var maxDuration = 1.0;
  var prefabDelay = 0.0;
  var vertexDelay = 0.01;

  for (var i = 0, offset = 0; i < prefabCount; i++) {
    delay = prefabDelay * i;
    duration = THREE.Math.randFloat(minDuration, maxDuration);
    
    for (var j = 0; j < geometry.prefabVertexCount; j++) {
      // by giving EACH VERTEX in a prefab its own delay (based on index) the prefabs are stretched out
      // as the animation plays
      aDelayDuration.array[offset++] = delay + vertexDelay * duration * j;
      aDelayDuration.array[offset++] = duration;
    }
  }
  
  this.totalDuration = maxDuration + prefabDelay * prefabCount + vertexDelay * geometry.prefabVertexCount;

  // position

  // start position is always (0, 0, 0)
  // this attribute could be removed, but I've kept it around for consistency
  geometry.createAttribute('aStartPosition', 3);
  // control positions and end position are filled inside the 'bufferPoints' method below
  geometry.createAttribute('aControlPosition0', 3);
  geometry.createAttribute('aControlPosition1', 3);
  geometry.createAttribute('aEndPosition', 3);

  // color

  // each prefab will have a tint of the gold-ish color #d7d2bf
  var colorObj = new THREE.Color('#d7d2bf');
  var colorHSL = colorObj.getHSL();
  var h, s, l;
  
  geometry.createAttribute('color', 3, function(data) {
    h = colorHSL.h;
    s = colorHSL.s;
    l = THREE.Math.randFloat(0.25, 1.00);
    colorObj.setHSL(h, s, l);
    
    colorObj.toArray(data);
  });
  
  // rotation
  
  var axis = new THREE.Vector3();
  
  geometry.createAttribute('aAxisAngle', 4, function(data) {
    BAS.Utils.randomAxis(axis).toArray(data);
    data[3] = Math.PI * THREE.Math.randFloat(8, 16);
  });
  
  var material = new BAS.BasicAnimationMaterial({
    side: THREE.DoubleSide,
    vertexColors: THREE.VertexColors,
    transparent: true,
    uniforms: {
      uTime: {value: 0.0}
    },
    vertexFunctions: [
      BAS.ShaderChunk['quaternion_rotation'],
      BAS.ShaderChunk['cubic_bezier'],
      BAS.ShaderChunk['ease_cubic_out']
    ],
    vertexParameters: [
      'uniform float uTime;',
  
      'attribute vec2 aDelayDuration;',
      'attribute vec3 aStartPosition;',
      'attribute vec3 aControlPosition0;',
      'attribute vec3 aControlPosition1;',
      'attribute vec3 aEndPosition;',
      'attribute vec4 aAxisAngle;'
    ],
    vertexPosition: [
      'float tProgress = clamp(uTime - aDelayDuration.x, 0.0, aDelayDuration.y) / aDelayDuration.y;',
      'tProgress = easeCubicOut(tProgress);',

      // rotate
      'vec4 tQuat = quatFromAxisAngle(aAxisAngle.xyz, aAxisAngle.w * tProgress);',
      'transformed = rotateVector(tQuat, transformed);',

      // scale (0.0 at start, 1.0 halfway, 0.0 at end of progress)
      'float scl = tProgress * 2.0 - 1.0;',
      'transformed *= (1.0 - scl * scl);',

      // translate
      'transformed += cubicBezier(aStartPosition, aControlPosition0, aControlPosition1, aEndPosition, tProgress);'
    ]
  });

  THREE.Mesh.call(this, geometry, material);
  this.frustumCulled = false;
  
  this.tween = TweenMax.fromTo(this.material.uniforms['uTime'], 1.0, {value: 0}, {
    value:this.totalDuration,
    ease:Power0.easeOut,
    onCompleteScope: this,
    onComplete: function() {
      this.dispatchEvent({type: 'tween_complete'});
    }
  });
  this.tween.pause();
}
701746
function createGeometry (data) {
  var geometryType = data.primitive;
  var GeometryClass = geometries[geometryType] && geometries[geometryType].Geometry;
  var geometryInstance = new GeometryClass();

  if (!GeometryClass) { throw new Error('Unknown geometry `' + geometryType + '`'); }

  geometryInstance.init(data);
  return toBufferGeometry(geometryInstance.geometry, data.buffer);
}
874217
function strokeRect(x, y, width, height) {
         if (this.backBufferContext2D.globalAlpha < 1 / 255) {
           // Fast path: don't draw fully transparent
           return;
         }

         this.backBufferContext2D.strokeRect(x, y, width, height);
       }
477449
public void drawGeometry(Geometry geometry, SymbolInfo symbol, Color fillColor, Color strokeColor, float lineWidth,
			float[] dashArray, Rectangle clipRect) {
		template.saveState();
		// clipping code
		if (clipRect != null) {
			template.rectangle(clipRect.getLeft() + origX, clipRect.getBottom() + origY, clipRect.getWidth(), clipRect
					.getHeight());
			template.clip();
			template.newPath();
		}
		setStroke(strokeColor, lineWidth, dashArray);
		setFill(fillColor);
		drawGeometry(geometry, symbol);
		template.restoreState();
	}
1272470
function(gl, vertices, uv) {
  var verticesBuf = createBuffer(gl, new Float32Array(vertices))
  var uvBuf = createBuffer(gl, new Float32Array(uv))

  var mesh = createVAO(gl, [
      { buffer: verticesBuf,
        size: 3
      },
      {
        buffer: uvBuf,
        size: 2
      }
      ])
  mesh.length = vertices.length/3

  return mesh
}
529975
protected void calculateDrawRectF() {
        mFillCircleRectF = getArcRect(mClockwiseReachedArcWidth);
        mReachedArcRectF = getArcRect(mClockwiseReachedArcWidth / 2);
        mOutlineArcRectF = getArcRect(mClockwiseOutlineArcWidth / 2);
    }
153416
function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {

	Geometry.call( this );

	this.type = 'TorusKnotGeometry';

	this.parameters = {
		radius: radius,
		tube: tube,
		tubularSegments: tubularSegments,
		radialSegments: radialSegments,
		p: p,
		q: q
	};

	if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );

	this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );
	this.mergeVertices();

}
3244
function Animation(modelGeometry) {
  var geometry = new BAS.ModelBufferGeometry(modelGeometry);

  var i, j;

  var aOffsetAmplitude = geometry.createAttribute('aOffsetAmplitude', 2);
  var positionBuffer = geometry.getAttribute('position').array;
  var x, y, distance;

  for (i = 0; i < aOffsetAmplitude.array.length; i += 12) { // 6 * 2
    var offset = THREE.Math.randFloat(1, 4);
    var amplitude = THREE.Math.randFloat(0.5, 1.0);

    x = 0;
    y = 0;

    // x/y position of the corresponding vertex from the position buffer
    for (j = 0; j < 6; j += 2) {
      x += positionBuffer[(i + j) / 2 * 3];
      y += positionBuffer[(i + j) / 2 * 3 + 1];
    }

    x /= 3;
    y /= 3;

    distance = Math.sqrt(x * x + y * y);

    for (j = 0; j < 12; j += 2) {
      aOffsetAmplitude.array[i + j]     = (distance + offset) * (1.0 + THREE.Math.randFloatSpread(0.0125));
      aOffsetAmplitude.array[i + j + 1] = amplitude;
    }
  }

  var aColor = geometry.createAttribute('color', 3);
  var color = new THREE.Color();

  for (i = 0; i < aColor.array.length; i += 18) { // 6 * 3
    color.setHSL(0, 0, THREE.Math.randFloat(0.5, 1.0));

    for (j = 0; j < 18; j += 3) {
      aColor.array[i + j]     = color.r;
      aColor.array[i + j + 1] = color.g;
      aColor.array[i + j + 2] = color.b;
    }
  }

  var material = new BAS.StandardAnimationMaterial({
    flatShading: true,
    vertexColors: THREE.VertexColors,
    transparent: true,
    side: THREE.DoubleSide,
    uniforms: {
      uTime: {value: 0},
      uD: {value: 4.4},
      uA: {value: 3.2}
    },
    uniformValues: {
      diffuse: new THREE.Color(0x9B111E),
      roughness: 0.2,
      metalness: 0.8,
      opacity: 0.8
    },
    vertexFunctions: [
      BAS.ShaderChunk['ease_cubic_in_out']
    ],
    vertexParameters: [
      'uniform float uTime;',
      'uniform float uD;',
      'uniform float uA;',
      'attribute vec2 aOffsetAmplitude;'
    ],
    vertexPosition: [
      'float tProgress = sin(uTime + aOffsetAmplitude.x / uD);',
      'tProgress = easeCubicInOut(tProgress);',
      'transformed.z += aOffsetAmplitude.y * uA * tProgress;'
    ]
  });

  geometry.computeVertexNormals();

  THREE.Mesh.call(this, geometry, material);

  this.frustumCulled = false;
}
1357566
function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {

	BufferGeometry.call( this );

	this.type = '';

	this.parameters = {
		radiusTop: radiusTop,
		radiusBottom: radiusBottom,
		height: height,
		radialSegments: radialSegments,
		heightSegments: heightSegments,
		openEnded: openEnded,
		thetaStart: thetaStart,
		thetaLength: thetaLength
	};

	var scope = this;

	radiusTop = radiusTop !== undefined ? radiusTop : 1;
	radiusBottom = radiusBottom !== undefined ? radiusBottom : 1;
	height = height || 1;

	radialSegments = Math.floor( radialSegments ) || 8;
	heightSegments = Math.floor( heightSegments ) || 1;

	openEnded = openEnded !== undefined ? openEnded : false;
	thetaStart = thetaStart !== undefined ? thetaStart : 0.0;
	thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;

	// buffers

	var indices = [];
	var vertices = [];
	var normals = [];
	var uvs = [];

	// helper variables

	var index = 0;
	var indexArray = [];
	var halfHeight = height / 2;
	var groupStart = 0;

	// generate geometry

	generateTorso();

	if ( openEnded === false ) {

		if ( radiusTop > 0 ) generateCap( true );
		if ( radiusBottom > 0 ) generateCap( false );

	}

	// build geometry

	this.setIndex( indices );
	this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
	this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
	this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );

	function generateTorso() {

		var x, y;
		var normal = new Vector3();
		var vertex = new Vector3();

		var groupCount = 0;

		// this will be used to calculate the normal
		var slope = ( radiusBottom - radiusTop ) / height;

		// generate vertices, normals and uvs

		for ( y = 0; y <= heightSegments; y ++ ) {

			var indexRow = [];

			var v = y / heightSegments;

			// calculate the radius of the current row

			var radius = v * ( radiusBottom - radiusTop ) + radiusTop;

			for ( x = 0; x <= radialSegments; x ++ ) {

				var u = x / radialSegments;

				var theta = u * thetaLength + thetaStart;

				var sinTheta = Math.sin( theta );
				var cosTheta = Math.cos( theta );

				// vertex

				vertex.x = radius * sinTheta;
				vertex.y = - v * height + halfHeight;
				vertex.z = radius * cosTheta;
				vertices.push( vertex.x, vertex.y, vertex.z );

				// normal

				normal.set( sinTheta, slope, cosTheta ).normalize();
				normals.push( normal.x, normal.y, normal.z );

				// uv

				uvs.push( u, 1 - v );

				// save index of vertex in respective row

				indexRow.push( index ++ );

			}

			// now save vertices of the row in our index array

			indexArray.push( indexRow );

		}

		// generate indices

		for ( x = 0; x < radialSegments; x ++ ) {

			for ( y = 0; y < heightSegments; y ++ ) {

				// we use the index array to access the correct indices

				var a = indexArray[ y ][ x ];
				var b = indexArray[ y + 1 ][ x ];
				var c = indexArray[ y + 1 ][ x + 1 ];
				var d = indexArray[ y ][ x + 1 ];

				// faces

				indices.push( a, b, d );
				indices.push( b, c, d );

				// update group counter

				groupCount += 6;

			}

		}

		// add a group to the geometry. this will ensure multi material support

		scope.addGroup( groupStart, groupCount, 0 );

		// calculate new start value for groups

		groupStart += groupCount;

	}

	function generateCap( top ) {

		var x, centerIndexStart, centerIndexEnd;

		var uv = new Vector2();
		var vertex = new Vector3();

		var groupCount = 0;

		var radius = ( top === true ) ? radiusTop : radiusBottom;
		var sign = ( top === true ) ? 1 : - 1;

		// save the index of the first center vertex
		centerIndexStart = index;

		// first we generate the center vertex data of the cap.
		// because the geometry needs one set of uvs per face,
		// we must generate a center vertex per face/segment

		for ( x = 1; x <= radialSegments; x ++ ) {

			// vertex

			vertices.push( 0, halfHeight * sign, 0 );

			// normal

			normals.push( 0, sign, 0 );

			// uv

			uvs.push( 0.5, 0.5 );

			// increase index

			index ++;

		}

		// save the index of the last center vertex

		centerIndexEnd = index;

		// now we generate the surrounding vertices, normals and uvs

		for ( x = 0; x <= radialSegments; x ++ ) {

			var u = x / radialSegments;
			var theta = u * thetaLength + thetaStart;

			var cosTheta = Math.cos( theta );
			var sinTheta = Math.sin( theta );

			// vertex

			vertex.x = radius * sinTheta;
			vertex.y = halfHeight * sign;
			vertex.z = radius * cosTheta;
			vertices.push( vertex.x, vertex.y, vertex.z );

			// normal

			normals.push( 0, sign, 0 );

			// uv

			uv.x = ( cosTheta * 0.5 ) + 0.5;
			uv.y = ( sinTheta * 0.5 * sign ) + 0.5;
			uvs.push( uv.x, uv.y );

			// increase index

			index ++;

		}

		// generate indices

		for ( x = 0; x < radialSegments; x ++ ) {

			var c = centerIndexStart + x;
			var i = centerIndexEnd + x;

			if ( top === true ) {

				// face top

				indices.push( i, i + 1, c );

			} else {

				// face bottom

				indices.push( i + 1, i, c );

			}

			groupCount += 3;

		}

		// add a group to the geometry. this will ensure multi material support

		scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );

		// calculate new start value for groups

		groupStart += groupCount;

	}

}