在产品需求中有管道展示的需求,管道展示有两个点需要注意,一是管道是具有宽度,需要有半径;二是管道拐角处理,需要圆滑。
1. TubeGeometry
由于管道需要半径,用Line就不合适。TubeGeometry正合适,拥有半径的参数控制。
### TubeGeometry(path : Curve, tubularSegments : Integer, radius : Float, radialSegments : Integer, closed : Boolean)
path — Curve - 一个由基类Curve继承而来的3D路径。 Default is a quadratic bezier curve.
tubularSegments — [Integer](<> "Integer") - 组成这一管道的分段数,默认值为64。
radius — [Float](<> "Float") - 管道的半径,默认值为1。
radialSegments — [Integer](<> "Integer") - 管道横截面的分段数目,默认值为8。
closed — [Boolean](<> "Boolean") 管道的两端是否闭合,默认值为false。
2. CatmullRomCurve3 VS CurvePath
管道的路径,是通过TubeGeometry的第一个参数设置的。实际使用的时候,定义的路径都是折线,相当于都是lineTo。由于管道是有半径的,如果直接折线,拐角就被折扁。所以需要对点进行处理。下面提供两种方式。
先假设存在点数组
let points = [
{"x": 0, "y": 0, "z": 0},
{"x": 15, "y": 0, "z": 0},
{"x": 15, "y": 20, "z": 0},
{"x": 15, "y": 20, "z": 20},
{"x": 35, "y": 20, "z": 20},
{"x": 55, "y": 0, "z": 0},
{"x": 75,"y": 0,"z": 0},
];
points = points.map(({x,y,z}) => new THREE.Vector3(x,y,z));
2.1 CatmullRomCurve3
CatmullRomCurve3使用Catmull-Rom算法, 从一系列的点创建一条平滑的三维样条曲线。
const curve = new THREE.CatmullRomCurve3(points);
curve.curveType = 'centripetal';
curve.closed = false;
const geometry1 = new THREE.TubeGeometry(curve, 200, 2, 10, false );
const material1 = new THREE.MeshPhongMaterial({ color: 0xfff000 });
const mesh1 = new THREE.Mesh( geometry1, material1 );
scene.add( mesh1 );
2.2 CurvePath
CurvePath可以理解成是Curve的集合,我们需要将points中的点转换为多个Curve,支持的Curve有CubicBezierCurve3、QuadraticBezierCurve3、LineCurve3。
getRoundCornerPath方法负责将points转换为Curve。
const path = new THREE.CurvePath();
getRoundCornerPath(points, 10, path)
const geometry = new THREE.TubeGeometry(path, 200, 2, 10, false );
const material = new THREE.MeshPhongMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
function getRoundCornerPath(points, radius, path) {
let p0 = points[0];
moveTo(path,p0)
let v1,v2 = p0;
for (let i = 1; i < points.length - 1; i++) {
let pre = points[i - 1],
p = points[i],
next = points[i + 1];
let sub1 = p.clone().sub(pre).setLength(radius),
sub2 = next.clone().sub(p).setLength(radius);
v1 = p.clone().sub(sub1);
v2 = p.clone().add(sub2);
// path.lineTo(v1);
// lineTo(path,v1,v1)
// path.curveTo(p.x, p.y, p.z, v2.x, v2.y, v2.z);
curveTo(path,v1,p,v2)
}
let pl = points[points.length - 1];
// path.lineTo(pl);
lineTo(path,v2,pl)
return path;
}
function moveTo(path,p0){
var curve = new THREE.LineCurve3(
new THREE.Vector3(p0.x, p0.y, p0.z),
new THREE.Vector3(p0.x, p0.y, p0.z)
);
path.add(curve)
}
function lineTo(path,p0,p1){
var curve = new THREE.LineCurve3(
new THREE.Vector3(p0.x, p0.y, p0.z),
new THREE.Vector3(p1.x, p1.y, p1.z)
);
path.add(curve)
}
function curveTo(path,p0,p1,p2){
var curve = new THREE.QuadraticBezierCurve3(
new THREE.Vector3(p0.x, p0.y, p0.z),
new THREE.Vector3(p1.x, p1.y, p1.z),
new THREE.Vector3(p2.x, p2.y, p2.z)
);
path.add(curve)
}
3. 效果
这是上面两种方式的效果,黄色是2.1的效果;绿色是2.2的效果。对比两种方式看不出差别
4. 注意
有个点需要注意,路径的长度比较长,但是TubeGeometry中的tubularSegments参数,设置的小的话,会让管道不丝滑。看下面的效果,黄色的设置tubularSegments值为20。这里建议通过路径长度计算出一个值。