{"size": 24655, "ext": "js", "max_stars_repo_path": "src/core/BufferGeometry.js", "max_stars_repo_name": "catallaxy-dev/three.js", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "src/core/BufferGeometry.js", "max_issues_repo_name": "catallaxy-dev/three.js", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "src/core/BufferGeometry.js", "max_forks_repo_name": "catallaxy-dev/three.js", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 23.3254493851, "max_line_length": 137, "alphanum_fraction": 0.6506996552}
import { Vector3 } from "../math/Vector3.js";
import { Box3 } from "../math/Box3.js";
import { EventDispatcher } from "./EventDispatcher.js";
import {
	BufferAttribute,
	Float32BufferAttribute,
	Uint16BufferAttribute,
	Uint32BufferAttribute,
} from "./BufferAttribute.js";
import { Sphere } from "../math/Sphere.js";
import { DirectGeometry } from "./DirectGeometry.js";
import { Object3D } from "./Object3D.js";
import { Matrix4 } from "../math/Matrix4.js";
import { Matrix3 } from "../math/Matrix3.js";
import { MathUtils } from "../math/MathUtils.js";
import { arrayMax } from "../utils.js";

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

let _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id

const _m1 = new Matrix4();
const _obj = new Object3D();
const _offset = new Vector3();
const _box = new Box3();
const _boxMorphTargets = new Box3();
const _vector = new Vector3();

function BufferGeometry() {
	Object.defineProperty(this, "id", { value: (_bufferGeometryId += 2) });

	this.uuid = MathUtils.generateUUID();

	this.name = "";
	this.type = "BufferGeometry";

	this.index = null;
	this.attributes = {};

	this.morphAttributes = {};
	this.morphTargetsRelative = false;

	this.groups = [];

	this.boundingBox = null;
	this.boundingSphere = null;

	this.drawRange = { start: 0, count: Infinity };

	this.userData = {};
}

BufferGeometry.prototype = Object.assign(
	Object.create(EventDispatcher.prototype),
	{
		constructor: BufferGeometry,

		isBufferGeometry: true,

		getIndex: function () {
			return this.index;
		},

		setIndex: function (index) {
			if (Array.isArray(index)) {
				this.index = new (arrayMax(index) > 65535
					? Uint32BufferAttribute
					: Uint16BufferAttribute)(index, 1);
			} else {
				this.index = index;
			}
		},

		getAttribute: function (name) {
			return this.attributes[name];
		},

		setAttribute: function (name, attribute) {
			this.attributes[name] = attribute;

			return this;
		},

		deleteAttribute: function (name) {
			delete this.attributes[name];

			return this;
		},

		addGroup: function (start, count, materialIndex) {
			this.groups.push({
				start: start,
				count: count,
				materialIndex: materialIndex !== undefined ? materialIndex : 0,
			});
		},

		clearGroups: function () {
			this.groups = [];
		},

		setDrawRange: function (start, count) {
			this.drawRange.start = start;
			this.drawRange.count = count;
		},

		applyMatrix4: function (matrix) {
			const position = this.attributes.position;

			if (position !== undefined) {
				position.applyMatrix4(matrix);

				position.needsUpdate = true;
			}

			const normal = this.attributes.normal;

			if (normal !== undefined) {
				const normalMatrix = new Matrix3().getNormalMatrix(matrix);

				normal.applyNormalMatrix(normalMatrix);

				normal.needsUpdate = true;
			}

			const tangent = this.attributes.tangent;

			if (tangent !== undefined) {
				tangent.transformDirection(matrix);

				tangent.needsUpdate = true;
			}

			if (this.boundingBox !== null) {
				this.computeBoundingBox();
			}

			if (this.boundingSphere !== null) {
				this.computeBoundingSphere();
			}

			return this;
		},

		rotateX: function (angle) {
			// rotate geometry around world x-axis

			_m1.makeRotationX(angle);

			this.applyMatrix4(_m1);

			return this;
		},

		rotateY: function (angle) {
			// rotate geometry around world y-axis

			_m1.makeRotationY(angle);

			this.applyMatrix4(_m1);

			return this;
		},

		rotateZ: function (angle) {
			// rotate geometry around world z-axis

			_m1.makeRotationZ(angle);

			this.applyMatrix4(_m1);

			return this;
		},

		translate: function (x, y, z) {
			// translate geometry

			_m1.makeTranslation(x, y, z);

			this.applyMatrix4(_m1);

			return this;
		},

		scale: function (x, y, z) {
			// scale geometry

			_m1.makeScale(x, y, z);

			this.applyMatrix4(_m1);

			return this;
		},

		lookAt: function (vector) {
			_obj.lookAt(vector);

			_obj.updateMatrix();

			this.applyMatrix4(_obj.matrix);

			return this;
		},

		center: function () {
			this.computeBoundingBox();

			this.boundingBox.getCenter(_offset).negate();

			this.translate(_offset.x, _offset.y, _offset.z);

			return this;
		},

		setFromObject: function (object) {
			// console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );

			const geometry = object.geometry;

			if (object.isPoints || object.isLine) {
				const positions = new Float32BufferAttribute(
					geometry.vertices.length * 3,
					3
				);
				const colors = new Float32BufferAttribute(
					geometry.colors.length * 3,
					3
				);

				this.setAttribute(
					"position",
					positions.copyVector3sArray(geometry.vertices)
				);
				this.setAttribute("color", colors.copyColorsArray(geometry.colors));

				if (
					geometry.lineDistances &&
					geometry.lineDistances.length === geometry.vertices.length
				) {
					const lineDistances = new Float32BufferAttribute(
						geometry.lineDistances.length,
						1
					);

					this.setAttribute(
						"lineDistance",
						lineDistances.copyArray(geometry.lineDistances)
					);
				}

				if (geometry.boundingSphere !== null) {
					this.boundingSphere = geometry.boundingSphere.clone();
				}

				if (geometry.boundingBox !== null) {
					this.boundingBox = geometry.boundingBox.clone();
				}
			} else if (object.isMesh) {
				if (geometry && geometry.isGeometry) {
					this.fromGeometry(geometry);
				}
			}

			return this;
		},

		setFromPoints: function (points) {
			const position = [];

			for (let i = 0, l = points.length; i < l; i++) {
				const point = points[i];
				position.push(point.x, point.y, point.z || 0);
			}

			this.setAttribute("position", new Float32BufferAttribute(position, 3));

			return this;
		},

		updateFromObject: function (object) {
			let geometry = object.geometry;

			if (object.isMesh) {
				let direct = geometry.__directGeometry;

				if (geometry.elementsNeedUpdate === true) {
					direct = undefined;
					geometry.elementsNeedUpdate = false;
				}

				if (direct === undefined) {
					return this.fromGeometry(geometry);
				}

				direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
				direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
				direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
				direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
				direct.groupsNeedUpdate = geometry.groupsNeedUpdate;

				geometry.verticesNeedUpdate = false;
				geometry.normalsNeedUpdate = false;
				geometry.colorsNeedUpdate = false;
				geometry.uvsNeedUpdate = false;
				geometry.groupsNeedUpdate = false;

				geometry = direct;
			}

			if (geometry.verticesNeedUpdate === true) {
				const attribute = this.attributes.position;

				if (attribute !== undefined) {
					attribute.copyVector3sArray(geometry.vertices);
					attribute.needsUpdate = true;
				}

				geometry.verticesNeedUpdate = false;
			}

			if (geometry.normalsNeedUpdate === true) {
				const attribute = this.attributes.normal;

				if (attribute !== undefined) {
					attribute.copyVector3sArray(geometry.normals);
					attribute.needsUpdate = true;
				}

				geometry.normalsNeedUpdate = false;
			}

			if (geometry.colorsNeedUpdate === true) {
				const attribute = this.attributes.color;

				if (attribute !== undefined) {
					attribute.copyColorsArray(geometry.colors);
					attribute.needsUpdate = true;
				}

				geometry.colorsNeedUpdate = false;
			}

			if (geometry.uvsNeedUpdate) {
				const attribute = this.attributes.uv;

				if (attribute !== undefined) {
					attribute.copyVector2sArray(geometry.uvs);
					attribute.needsUpdate = true;
				}

				geometry.uvsNeedUpdate = false;
			}

			if (geometry.lineDistancesNeedUpdate) {
				const attribute = this.attributes.lineDistance;

				if (attribute !== undefined) {
					attribute.copyArray(geometry.lineDistances);
					attribute.needsUpdate = true;
				}

				geometry.lineDistancesNeedUpdate = false;
			}

			if (geometry.groupsNeedUpdate) {
				geometry.computeGroups(object.geometry);
				this.groups = geometry.groups;

				geometry.groupsNeedUpdate = false;
			}

			return this;
		},

		fromGeometry: function (geometry) {
			geometry.__directGeometry = new DirectGeometry().fromGeometry(geometry);

			return this.fromDirectGeometry(geometry.__directGeometry);
		},

		fromDirectGeometry: function (geometry) {
			const positions = new Float32Array(geometry.vertices.length * 3);
			this.setAttribute(
				"position",
				new BufferAttribute(positions, 3).copyVector3sArray(geometry.vertices)
			);

			if (geometry.normals.length > 0) {
				const normals = new Float32Array(geometry.normals.length * 3);
				this.setAttribute(
					"normal",
					new BufferAttribute(normals, 3).copyVector3sArray(geometry.normals)
				);
			}

			if (geometry.colors.length > 0) {
				const colors = new Float32Array(geometry.colors.length * 3);
				this.setAttribute(
					"color",
					new BufferAttribute(colors, 3).copyColorsArray(geometry.colors)
				);
			}

			if (geometry.uvs.length > 0) {
				const uvs = new Float32Array(geometry.uvs.length * 2);
				this.setAttribute(
					"uv",
					new BufferAttribute(uvs, 2).copyVector2sArray(geometry.uvs)
				);
			}

			if (geometry.uvs2.length > 0) {
				const uvs2 = new Float32Array(geometry.uvs2.length * 2);
				this.setAttribute(
					"uv2",
					new BufferAttribute(uvs2, 2).copyVector2sArray(geometry.uvs2)
				);
			}

			// groups

			this.groups = geometry.groups;

			// morphs

			for (let name in geometry.morphTargets) {
				const array = [];
				const morphTargets = geometry.morphTargets[name];

				for (let i = 0, l = morphTargets.length; i < l; i++) {
					const morphTarget = morphTargets[i];

					const attribute = new Float32BufferAttribute(
						morphTarget.data.length * 3,
						3
					);
					attribute.name = morphTarget.name;

					array.push(attribute.copyVector3sArray(morphTarget.data));
				}

				this.morphAttributes[name] = array;
			}

			// skinning

			if (geometry.skinIndices.length > 0) {
				const skinIndices = new Float32BufferAttribute(
					geometry.skinIndices.length * 4,
					4
				);
				this.setAttribute(
					"skinIndex",
					skinIndices.copyVector4sArray(geometry.skinIndices)
				);
			}

			if (geometry.skinWeights.length > 0) {
				const skinWeights = new Float32BufferAttribute(
					geometry.skinWeights.length * 4,
					4
				);
				this.setAttribute(
					"skinWeight",
					skinWeights.copyVector4sArray(geometry.skinWeights)
				);
			}

			//

			if (geometry.boundingSphere !== null) {
				this.boundingSphere = geometry.boundingSphere.clone();
			}

			if (geometry.boundingBox !== null) {
				this.boundingBox = geometry.boundingBox.clone();
			}

			return this;
		},

		computeBoundingBox: function () {
			if (this.boundingBox === null) {
				this.boundingBox = new Box3();
			}

			const position = this.attributes.position;
			const morphAttributesPosition = this.morphAttributes.position;

			if (position !== undefined) {
				this.boundingBox.setFromBufferAttribute(position);

				// process morph attributes if present

				if (morphAttributesPosition) {
					for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
						const morphAttribute = morphAttributesPosition[i];
						_box.setFromBufferAttribute(morphAttribute);

						if (this.morphTargetsRelative) {
							_vector.addVectors(this.boundingBox.min, _box.min);
							this.boundingBox.expandByPoint(_vector);

							_vector.addVectors(this.boundingBox.max, _box.max);
							this.boundingBox.expandByPoint(_vector);
						} else {
							this.boundingBox.expandByPoint(_box.min);
							this.boundingBox.expandByPoint(_box.max);
						}
					}
				}
			} else {
				this.boundingBox.makeEmpty();
			}

			if (
				isNaN(this.boundingBox.min.x) ||
				isNaN(this.boundingBox.min.y) ||
				isNaN(this.boundingBox.min.z)
			) {
				console.error(
					'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.',
					this
				);
			}
		},

		computeBoundingSphere: function () {
			if (this.boundingSphere === null) {
				this.boundingSphere = new Sphere();
			}

			const position = this.attributes.position;
			const morphAttributesPosition = this.morphAttributes.position;

			if (position) {
				// first, find the center of the bounding sphere

				const center = this.boundingSphere.center;

				_box.setFromBufferAttribute(position);

				// process morph attributes if present

				if (morphAttributesPosition) {
					for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
						const morphAttribute = morphAttributesPosition[i];
						_boxMorphTargets.setFromBufferAttribute(morphAttribute);

						if (this.morphTargetsRelative) {
							_vector.addVectors(_box.min, _boxMorphTargets.min);
							_box.expandByPoint(_vector);

							_vector.addVectors(_box.max, _boxMorphTargets.max);
							_box.expandByPoint(_vector);
						} else {
							_box.expandByPoint(_boxMorphTargets.min);
							_box.expandByPoint(_boxMorphTargets.max);
						}
					}
				}

				_box.getCenter(center);

				// second, try to find a boundingSphere with a radius smaller than the
				// boundingSphere of the boundingBox: sqrt(3) smaller in the best case

				let maxRadiusSq = 0;

				for (let i = 0, il = position.count; i < il; i++) {
					_vector.fromBufferAttribute(position, i);

					maxRadiusSq = Math.max(
						maxRadiusSq,
						center.distanceToSquared(_vector)
					);
				}

				// process morph attributes if present

				if (morphAttributesPosition) {
					for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
						const morphAttribute = morphAttributesPosition[i];
						const morphTargetsRelative = this.morphTargetsRelative;

						for (let j = 0, jl = morphAttribute.count; j < jl; j++) {
							_vector.fromBufferAttribute(morphAttribute, j);

							if (morphTargetsRelative) {
								_offset.fromBufferAttribute(position, j);
								_vector.add(_offset);
							}

							maxRadiusSq = Math.max(
								maxRadiusSq,
								center.distanceToSquared(_vector)
							);
						}
					}
				}

				this.boundingSphere.radius = Math.sqrt(maxRadiusSq);

				if (isNaN(this.boundingSphere.radius)) {
					console.error(
						'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.',
						this
					);
				}
			}
		},

		computeFaceNormals: function () {
			// backwards compatibility
		},

		computeVertexNormals: function () {
			const index = this.index;
			const positionAttribute = this.getAttribute("position");

			if (positionAttribute !== undefined) {
				let normalAttribute = this.getAttribute("normal");

				if (normalAttribute === undefined) {
					normalAttribute = new BufferAttribute(
						new Float32Array(positionAttribute.count * 3),
						3
					);
					this.setAttribute("normal", normalAttribute);
				} else {
					// reset existing normals to zero

					for (let i = 0, il = normalAttribute.count; i < il; i++) {
						normalAttribute.setXYZ(i, 0, 0, 0);
					}
				}

				const pA = new Vector3(),
					pB = new Vector3(),
					pC = new Vector3();
				const nA = new Vector3(),
					nB = new Vector3(),
					nC = new Vector3();
				const cb = new Vector3(),
					ab = new Vector3();

				// indexed elements

				if (index) {
					for (let i = 0, il = index.count; i < il; i += 3) {
						const vA = index.getX(i + 0);
						const vB = index.getX(i + 1);
						const vC = index.getX(i + 2);

						pA.fromBufferAttribute(positionAttribute, vA);
						pB.fromBufferAttribute(positionAttribute, vB);
						pC.fromBufferAttribute(positionAttribute, vC);

						cb.subVectors(pC, pB);
						ab.subVectors(pA, pB);
						cb.cross(ab);

						nA.fromBufferAttribute(normalAttribute, vA);
						nB.fromBufferAttribute(normalAttribute, vB);
						nC.fromBufferAttribute(normalAttribute, vC);

						nA.add(cb);
						nB.add(cb);
						nC.add(cb);

						normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z);
						normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z);
						normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z);
					}
				} else {
					// non-indexed elements (unconnected triangle soup)

					for (let i = 0, il = positionAttribute.count; i < il; i += 3) {
						pA.fromBufferAttribute(positionAttribute, i + 0);
						pB.fromBufferAttribute(positionAttribute, i + 1);
						pC.fromBufferAttribute(positionAttribute, i + 2);

						cb.subVectors(pC, pB);
						ab.subVectors(pA, pB);
						cb.cross(ab);

						normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z);
						normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z);
						normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z);
					}
				}

				this.normalizeNormals();

				normalAttribute.needsUpdate = true;
			}
		},

		merge: function (geometry, offset) {
			if (!(geometry && geometry.isBufferGeometry)) {
				console.error(
					"THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.",
					geometry
				);
				return;
			}

			if (offset === undefined) {
				offset = 0;

				console.warn(
					"THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. " +
						"Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge."
				);
			}

			const attributes = this.attributes;

			for (let key in attributes) {
				if (geometry.attributes[key] === undefined) continue;

				const attribute1 = attributes[key];
				const attributeArray1 = attribute1.array;

				const attribute2 = geometry.attributes[key];
				const attributeArray2 = attribute2.array;

				const attributeOffset = attribute2.itemSize * offset;
				const length = Math.min(
					attributeArray2.length,
					attributeArray1.length - attributeOffset
				);

				for (let i = 0, j = attributeOffset; i < length; i++, j++) {
					attributeArray1[j] = attributeArray2[i];
				}
			}

			return this;
		},

		normalizeNormals: function () {
			const normals = this.attributes.normal;

			for (let i = 0, il = normals.count; i < il; i++) {
				_vector.fromBufferAttribute(normals, i);

				_vector.normalize();

				normals.setXYZ(i, _vector.x, _vector.y, _vector.z);
			}
		},

		toNonIndexed: function () {
			function convertBufferAttribute(attribute, indices) {
				const array = attribute.array;
				const itemSize = attribute.itemSize;
				const normalized = attribute.normalized;

				const array2 = new array.constructor(indices.length * itemSize);

				let index = 0,
					index2 = 0;

				for (let i = 0, l = indices.length; i < l; i++) {
					index = indices[i] * itemSize;

					for (let j = 0; j < itemSize; j++) {
						array2[index2++] = array[index++];
					}
				}

				return new BufferAttribute(array2, itemSize, normalized);
			}

			//

			if (this.index === null) {
				console.warn(
					"THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed."
				);
				return this;
			}

			const geometry2 = new BufferGeometry();

			const indices = this.index.array;
			const attributes = this.attributes;

			// attributes

			for (let name in attributes) {
				const attribute = attributes[name];

				const newAttribute = convertBufferAttribute(attribute, indices);

				geometry2.setAttribute(name, newAttribute);
			}

			// morph attributes

			const morphAttributes = this.morphAttributes;

			for (let name in morphAttributes) {
				const morphArray = [];
				const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes

				for (let i = 0, il = morphAttribute.length; i < il; i++) {
					const attribute = morphAttribute[i];

					const newAttribute = convertBufferAttribute(attribute, indices);

					morphArray.push(newAttribute);
				}

				geometry2.morphAttributes[name] = morphArray;
			}

			geometry2.morphTargetsRelative = this.morphTargetsRelative;

			// groups

			const groups = this.groups;

			for (let i = 0, l = groups.length; i < l; i++) {
				const group = groups[i];
				geometry2.addGroup(group.start, group.count, group.materialIndex);
			}

			return geometry2;
		},

		toJSON: function () {
			const data = {
				metadata: {
					version: 4.5,
					type: "BufferGeometry",
					generator: "BufferGeometry.toJSON",
				},
			};

			// standard BufferGeometry serialization

			data.uuid = this.uuid;
			data.type = this.type;
			if (this.name !== "") data.name = this.name;
			if (Object.keys(this.userData).length > 0) data.userData = this.userData;

			if (this.parameters !== undefined) {
				const parameters = this.parameters;

				for (let key in parameters) {
					if (parameters[key] !== undefined) data[key] = parameters[key];
				}

				return data;
			}

			data.data = { attributes: {} };

			const index = this.index;

			if (index !== null) {
				data.data.index = {
					type: index.array.constructor.name,
					array: Array.prototype.slice.call(index.array),
				};
			}

			const attributes = this.attributes;

			for (let key in attributes) {
				const attribute = attributes[key];

				const attributeData = attribute.toJSON(data.data);

				if (attribute.name !== "") attributeData.name = attribute.name;

				data.data.attributes[key] = attributeData;
			}

			const morphAttributes = {};
			let hasMorphAttributes = false;

			for (let key in this.morphAttributes) {
				const attributeArray = this.morphAttributes[key];

				const array = [];

				for (let i = 0, il = attributeArray.length; i < il; i++) {
					const attribute = attributeArray[i];

					const attributeData = attribute.toJSON(data.data);

					if (attribute.name !== "") attributeData.name = attribute.name;

					array.push(attributeData);
				}

				if (array.length > 0) {
					morphAttributes[key] = array;

					hasMorphAttributes = true;
				}
			}

			if (hasMorphAttributes) {
				data.data.morphAttributes = morphAttributes;
				data.data.morphTargetsRelative = this.morphTargetsRelative;
			}

			const groups = this.groups;

			if (groups.length > 0) {
				data.data.groups = JSON.parse(JSON.stringify(groups));
			}

			const boundingSphere = this.boundingSphere;

			if (boundingSphere !== null) {
				data.data.boundingSphere = {
					center: boundingSphere.center.toArray(),
					radius: boundingSphere.radius,
				};
			}

			return data;
		},

		clone: function () {
			/*
		 // Handle primitives

		 const parameters = this.parameters;

		 if ( parameters !== undefined ) {

		 const values = [];

		 for ( let key in parameters ) {

		 values.push( parameters[ key ] );

		 }

		 const geometry = Object.create( this.constructor.prototype );
		 this.constructor.apply( geometry, values );
		 return geometry;

		 }

		 return new this.constructor().copy( this );
		 */

			return new BufferGeometry().copy(this);
		},

		copy: function (source) {
			// reset

			this.index = null;
			this.attributes = {};
			this.morphAttributes = {};
			this.groups = [];
			this.boundingBox = null;
			this.boundingSphere = null;

			// used for storing cloned, shared data

			const data = {};

			// name

			this.name = source.name;

			// index

			const index = source.index;

			if (index !== null) {
				this.setIndex(index.clone(data));
			}

			// attributes

			const attributes = source.attributes;

			for (let name in attributes) {
				const attribute = attributes[name];
				this.setAttribute(name, attribute.clone(data));
			}

			// morph attributes

			const morphAttributes = source.morphAttributes;

			for (let name in morphAttributes) {
				const array = [];
				const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes

				for (let i = 0, l = morphAttribute.length; i < l; i++) {
					array.push(morphAttribute[i].clone(data));
				}

				this.morphAttributes[name] = array;
			}

			this.morphTargetsRelative = source.morphTargetsRelative;

			// groups

			const groups = source.groups;

			for (let i = 0, l = groups.length; i < l; i++) {
				const group = groups[i];
				this.addGroup(group.start, group.count, group.materialIndex);
			}

			// bounding box

			const boundingBox = source.boundingBox;

			if (boundingBox !== null) {
				this.boundingBox = boundingBox.clone();
			}

			// bounding sphere

			const boundingSphere = source.boundingSphere;

			if (boundingSphere !== null) {
				this.boundingSphere = boundingSphere.clone();
			}

			// draw range

			this.drawRange.start = source.drawRange.start;
			this.drawRange.count = source.drawRange.count;

			// user data

			this.userData = source.userData;

			return this;
		},

		dispose: function () {
			this.dispatchEvent({ type: "dispose" });
		},
	}
);

export { BufferGeometry };
