是否可以使用类扩展Three.JS Mesh?

时间:2018-08-27 14:04:27

标签: javascript three.js

据我所知,ECMAscript 6类语法只是先前使用的原型继承模式的语法糖,但是我对如何扩展Three.js Mesh对象与类语法一起使用感到困惑。

此刻,我有一个基于原型模式的工作类:

function GFAElement(params) {
    //
    var p = params.split("-");
    var userData = {
        type:    "GFA",
        flights: parseInt(p[0]),
        pitch:   parseInt(p[1]),
        length:  parseInt(p[2])
    };

    var shape = getProfile();

    var extrudeSettings = {
        steps: 5*userData.length,
        depth: userData.length,
        bevelEnabled: false
    };

    var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
    var material = new THREE.MeshStandardMaterial( {
        color: 0xffffff,
        metalness: 0.5,
        roughness: 0.5,
    } );

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

    this.geometry.vertices.forEach( vertex => {
        var angle = 2*Math.PI/userData.flights*vertex.z/userData.pitch;
        var updateX = vertex.x * Math.cos(angle) - vertex.y * Math.sin(angle);
        var updateY = vertex.y * Math.cos(angle) + vertex.x * Math.sin(angle);
        vertex.x = updateX;
        vertex.y = updateY;
    });
    this.geometry.computeFaceNormals();
    this.geometry.computeVertexNormals();

    this.type = 'GFAElement';
    this.userData = userData;

}

GFAElement.prototype = Object.create( THREE.Mesh.prototype );
GFAElement.prototype.constructor = GFAElement;

我已经尝试将其转换为类语法:

class GFAElement extends THREE.Mesh {

    constructor(params){
        super();

        this.type = 'GFAElement';

        var p = params.split("-");
        this.userData.type = "GFA",
        this.userData.flights = parseInt(p[0]);
        this.userData.pitch   = parseInt(p[1]);
        this.userData.length  = parseInt(p[2]);

        this.generate();

    }

    generate(){

        var shape = getProfile();

        var extrudeSettings = {
            steps: 5*this.userData.length,
            depth: this.userData.length,
            bevelEnabled: false
        };

        var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
        var material = new THREE.MeshStandardMaterial( {
            color: 0xffffff,
            metalness: 0.5,
            roughness: 0.5,
        } );

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

        this.geometry.vertices.forEach( vertex => {
            var angle = 2*Math.PI/this.userData.flights*vertex.z/this.userData.pitch;
            var updateX = vertex.x * Math.cos(angle) - vertex.y * Math.sin(angle);
            var updateY = vertex.y * Math.cos(angle) + vertex.x * Math.sin(angle);
            vertex.x = updateX;
            vertex.y = updateY;
        });
        this.geometry.computeFaceNormals();
        this.geometry.computeVertexNormals();

    }

}

这给我一个错误:

three.min.js:11 Uncaught TypeError: Cannot redefine property: id

我认为这是extends THREE.MeshTHREE.Mesh.call( this, geometry, material );不兼容的结果,但是我无法纠正这种不兼容。

这使我有一个主要问题,我该如何将上述原型模式转换为类语法?

'use strict';

var container;
var camera, scene, renderer, controls;
var screw, mirror;

// Screw parameters
var P = 2; // number of flights

var D = 50, // outer diameter
		Dr = D/1.66, // root diameter
		Cl = (Dr+D)/2, // centerline distance
		αi = 2*Math.acos(Cl/D),
		Ih = D*Math.sin(αi/2)/2,
		H = D-Cl;

var αf = αi,
		αt = Math.PI/P - αf,
		αr = αt;

//console.log(D, Dr, Cl, Ih, H);
//console.log(αi, αf, αt, αr);

function getFlankParams(α1, D1, α2, D2, ctr){
	// flanks are arcs with origin (xc, yc) of radius Cl passing through (x1, y1) and (x2, y2):
	// (x1-xc)^2 + (y1-yc)^2 = Cl^2
	// (x2-xc)^2 + (y2-yc)^2 = Cl^2
	var x1 = D1*Math.cos(α1),
			y1 = D1*Math.sin(α1),
			x2 = D2*Math.cos(α2),
			y2 = D2*Math.sin(α2);
	// Solving system of equations yields linear eq:
	// y1-yc = beta - alpha*(x1-xc)
	var alpha = (x1-x2)/(y1-y2),	
			beta = (y1-y2)*(1+Math.pow(alpha,2))/2;
	// Substitution and applying quadratic equation:
	var xc = x1 - alpha*beta/(1+Math.pow(alpha,2))*(1+Math.pow(-1,ctr)*Math.sqrt(1-(1-Math.pow(Cl/beta,2))*(1+1/Math.pow(alpha,2)))),
			yc = y1 + alpha*(x1-xc) - beta;
	// Following from law of consines, the angle the flank extends wrt its own origin:
	var asq = Math.pow(Dr/2,2)+Math.pow(D/2,2)-2*(Dr/2)*(D/2)*Math.cos(αf),
			af = Math.acos(1-asq/Math.pow(Cl, 2)/2);

	var params = {xc: xc, yc: yc, af: af};
	return params;
}

function getProfile() {

	var shape = new THREE.Shape();
	var angle = 0, ctr = 0;
	// loop over number of flights
	for (var p=0; p<P; p++){
		// tip
		shape.absarc(0, 0, D/2, angle, angle+αt);
		angle += αt; 
		// flank
		var params = getFlankParams(angle, D/2, angle+αf, Dr/2, ctr++);
		shape.absarc(params.xc, params.yc, Cl, angle+αf-params.af, angle+αf, false);
		angle += αf; 
		// root
		shape.absarc(0, 0, Dr/2, angle, angle+αr);
		angle += αr; 
		// flank
		params = getFlankParams(angle, Dr/2, angle+αf, D/2, ctr++);
		shape.absarc(params.xc, params.yc, Cl, angle, angle+αf-params.af, false);
		angle += αf;
	}
	return shape;

}

function GFAElement(params) {
	//
	var p = params.split("-");
	var userData = {
		type: 	 "GFA",
		flights: parseInt(p[0]),
		pitch:   parseInt(p[1]),
		length:  parseInt(p[2])
	};

	var shape = getProfile();

	var extrudeSettings = {
		steps: 5*userData.length,
		depth: userData.length,
		bevelEnabled: false
	};

	var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
	var material = new THREE.MeshStandardMaterial( {
		color: 0xffffff,
		metalness: 0.5,
		roughness: 0.5,
	} );

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

	this.geometry.vertices.forEach( vertex => {
		var angle = 2*Math.PI/userData.flights*vertex.z/userData.pitch;
		var updateX = vertex.x * Math.cos(angle) - vertex.y * Math.sin(angle);
		var updateY = vertex.y * Math.cos(angle) + vertex.x * Math.sin(angle);
		vertex.x = updateX;
		vertex.y = updateY;
	});
	this.geometry.computeFaceNormals();
	this.geometry.computeVertexNormals();

	this.type = 'GFAElement';
	this.userData = userData;
		
}

GFAElement.prototype = Object.create( THREE.Mesh.prototype );
GFAElement.prototype.constructor = GFAElement;
//GFAElement.prototype.getMesh = function() {return this.mesh;}

class NewGFAElement extends THREE.Mesh {

	constructor(params){
		super();
		
		this.type = 'GFAElement';
		
		var p = params.split("-");
		this.userData.type = "GFA",
		this.userData.flights = parseInt(p[0]);
		this.userData.pitch   = parseInt(p[1]);
		this.userData.length  = parseInt(p[2]);
		
		this.generate();
		
	}

	generate(){
	
		var shape = getProfile();

		var extrudeSettings = {
			steps: 5*this.userData.length,
			depth: this.userData.length,
			bevelEnabled: false
		};

		var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
		var material = new THREE.MeshStandardMaterial( {
			color: 0xffffff,
			metalness: 0.5,
			roughness: 0.5,
		} );

		THREE.Mesh.call( this, geometry, material );
		
		this.geometry.vertices.forEach( vertex => {
			var angle = 2*Math.PI/this.userData.flights*vertex.z/this.userData.pitch;
			var updateX = vertex.x * Math.cos(angle) - vertex.y * Math.sin(angle);
			var updateY = vertex.y * Math.cos(angle) + vertex.x * Math.sin(angle);
			vertex.x = updateX;
			vertex.y = updateY;
		});
		this.geometry.computeFaceNormals();
		this.geometry.computeVertexNormals();
		
	}

}

class KBElement extends THREE.Group {
	//
	constructor(params){
		super();
			
		this.type = 'KBElement';

		var p = params.split("-");
		this.userData.type = "KB";
		this.userData.thickness = parseInt(p[0]);
		this.userData.flights   = parseInt(p[1]);
		this.userData.length    = parseInt(p[2]);
		this.userData.stagAngle = parseInt(p[3]);

		this.generate();
	}
	
	generate(){
	
		var shape = getProfile();

		var extrudeSettings = {
			depth: this.userData.thickness,
			bevelEnabled: false
		};

		var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
		var material = new THREE.MeshStandardMaterial( {
			color: 0xffffff,
			metalness: 0.5,
			roughness: 0.5,
		} );

		var mesh = new THREE.Mesh( geometry, material );

		super.add( mesh );
		for (var n=1, nt = this.userData.length/this.userData.thickness; n<nt; n++){
			mesh = mesh.clone();
			mesh.position.z += this.userData.thickness;
			mesh.rotation.z += this.userData.stagAngle;
			super.add( mesh );
		}
		
	}
	
}

class Screw extends THREE.Group {
	//
	constructor(){
		super();
	}

	add(desc){
		var elem;
		var params = desc.split(" ");
		if (params[0] == "GFA") {
			elem = new NewGFAElement(params[1]);
		} else
		if (params[0] == "KB") {
			elem = new KBElement(params[1]);
		}
		super.add(elem);
	}

}

function init() {
	
	renderer = new THREE.WebGLRenderer();
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	//renderer.gammaInput = true;
	//renderer.gammaOutput = true;
	document.body.appendChild( renderer.domElement );
	
	scene = new THREE.Scene();
	scene.background = new THREE.Color( 0x222222 );

	camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
	camera.position.set( 200, 200, 200 );
  scene.add( camera );
	
	var light = new THREE.PointLight( 0xffffff );
	camera.add( light );
	
	controls = new THREE.TrackballControls( camera, renderer.domElement );
	controls.minDistance = 100;
	controls.maxDistance = 500;

	screw = new Screw();
	screw.add('GFA 2-40-90');
	//screw.add('KB 5-2-30-90');
	
	//var mirror = screw.mirror();
	//scene.add(screw, mirror);
  scene.add(screw);
	
	console.log(screw);
	
}

function animate() {
	
	//group.rotation.z 	-= 2*Math.PI/100;
	//mirror.rotation.z -= 2*Math.PI/100;

	requestAnimationFrame( animate );
	controls.update();
	renderer.render( scene, camera );
}

init();
animate();
			body {
				font-family: Monospace;
				background-color: #222;
				margin: 0px;
				overflow: hidden;
			}
			a {
				color: #f80;
			}
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>

1 个答案:

答案 0 :(得分:1)

在访问super()的构造函数中的this引用之前,必须确保调用GFAElement。因此,首先创建几何图形和材料,然后像这样调用super

super( geometry, material );

以下小提琴在基于您的代码的完整示例中展示了该方法:

https://jsfiddle.net/f2Lommf5/12523/