this.phi0 = alpha; // 在时间t=0,将此齿轮旋转角度α newGear.phi0 = alpha + Math.PI + (Math.PI / newTeeth); // 同时(t=0),旋转新齿轮角度(180-α),对着第一个齿轮 // 并且加上一半的齿轮旋转使它们的轮齿能够啮合 newGear.createdAt = this.createdAt; // 当然,还得同步它们的时钟
上面的做法是有效的。然而,这种方法的缺点是我们在一直改变 this.phi ,这意味着以前与之同步的任何其他齿轮将不再同步。
每次 this.phi 都被一些 delta 更新,那么 newGear.phi 应该被更新多少?答案是 delta * (newGear.angularSpeed / this.angularSpeed) ,因为要考虑齿轮转速之比。了解到这个之后,我们可以把两个齿轮都更新一下: delta = (this.phi0 - alpha) ,以消除这种影响:
// 在时间t=0,将此齿轮旋转角度α this.phi0 = alpha + (this.phi0 - alpha); // this.phi0,没啥用,仅供展示。 newGear.phi0 = alpha + Math.PI + (Math.PI / newTeeth) + (this.phi0 - alpha) * (newGear.angularSpeed / this.angularSpeed); // 同时(t=0),旋转新齿轮角度(180-α),对着第一个齿轮 // 并且加上一半的齿轮旋转使它们的轮齿能够啮合 newGear.createdAt = this.createdAt; // 当然,还得同步它们的时钟
通过这样的处理, this.phi0 保持不变,不过另一个齿轮却得以同步:
See the Pen Gear-2-3 by molunerfinn ( @molunerfinn ) on CodePen .
第三部分 3D齿轮在上一个部分,我们已经展示了如何通过HTML5 Canvas渲染出啮合的齿轮对。本部分,我们将会探索通过使用Three.js这个库,渲染出完全3D的JavaScript齿轮,同时也会进一步扩大视图的复杂性。
3D渲染Three.js是一个用于3D图形渲染的JavaScript库,是基于WebGL的。它是免费,开放,并且不断更新的。我们将从移植我们的渲染代码开始,首先试着描绘出2D平面里的齿轮轮廓。
var shape = new THREE.Shape(); // 跳转到齿轮顶部的起始位置 var x0 = this.radius; var y0 = 0; shape.moveTo(x0, y0); for (var i = 0; i < this.legs * 2; i++) { var alpha = 2 * Math.PI * (i / (this.legs * 2)) + this.phi; var x = Math.cos(alpha) * this.radius; var y = Math.sin(alpha) * this.radius; createArc(shape, x, y, this.connectionRadius, -Math.PI / 2 + alpha, // 起始角度 Math.PI / 2 + alpha, // 结束角度 i % 2 == 0, // 顺时针还是逆时针 3 // 每段弧的离散段数 ); } return shape;
我用了一个辅助函数把弧形段分割成离散的段:
function createArc(shape, x, y, radius, from, to, sign, parts) { var src = sign ? from : to; // 确保我们总是沿着一个方向移动 var trg = sign ? to : from; var delta = sign ? 0 : Math.PI; // 但在需要的时候可以反转角度 for (var i = 1; i < parts; i++) { var t = i / parts; var cx = x + radius * Math.cos(delta + (src * (1 - t) + trg * t)); var cy = y + radius * Math.sin(delta + (src * (1 - t) + trg * t)); shape.lineTo(cx, cy); } }
当前的代码里有一个问题——啮合的齿轮会从一个小角度phi开始旋转,而不是恰好在 (r,0) 。要正确地获取 (x0,y0) 需要进行以下调整:
var sign = this.legs % 2; var from = -Math.PI / 2 + this.phi; var to = Math.PI / 2 + this.phi; var src = sign ? from : to; var trg = sign ? to : from; var delta = sign ? 0 : Math.PI; var x0 = Math.cos(this.phi)*this.radius + this.connectionRadius*Math.cos(delta + src); var y0 = Math.sin(this.phi)*this.radius + this.connectionRadius*Math.sin(delta + src);
现在,让我们增加一些代码来渲染齿轮中心的孔:
var holePath = new THREE.Path(); holePath.moveTo(this.connectionRadius, 0); createArc(holePath, 0, 0, this.connectionRadius, 0, 2 * Math.PI, true, 10); this.shape.holes.push(holePath);
一旦我们齿轮的轮廓被描绘,我们可以使用Three.js的拉伸功能使其变得更加真实起来:
var extrudeSettings = { steps: 1, amount: gearDepth, bevelEnabled: false, }; var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); var material = new THREE.MeshPhongMaterial({ color: color, polygonOffset: true, polygonOffsetFactor: 1.0, polygonOffsetUnits: 4.0 }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh);
这里是结果:
See the Pen Gear-3-1 by molunerfinn ( @molunerfinn ) on CodePen .
生成更复杂的场景使用第二部分的代码我们可以生成一对互相啮合的齿轮,为了生成更复杂的场景,只要我们避免碰撞,可以一次添加一个齿轮。