用三角形画金字塔



我正在尝试编码一个Pyramix,它是一个由多个三角形组成的四面体。我做这件事的方式一定不太准确。这是我的代码,也可以在https://codepen.io/jeffprod/pen/XWbBZLN。

问题是我写的facesVectors坐标很好。黄色和蓝色的那一面似乎还可以。但是设置红色和绿色三角形的位置会很困难。

有简单的方法吗?

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(-2, 1, 3);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new THREE.OrbitControls(camera, renderer.domElement);
// one triangle equilateral
const sideLength = 1
const x = 0
const y = 0
const geometry = new THREE.Geometry()
geometry.vertices.push(new THREE.Vector3(x, (Math.sqrt(3) / 2 * sideLength) - (sideLength / 2), 0))
geometry.vertices.push(new THREE.Vector3(x - (sideLength / 2), y - (sideLength / 2), 0))
geometry.vertices.push(new THREE.Vector3(x + (sideLength / 2), y - (sideLength / 2), 0))
geometry.faces.push(new THREE.Face3(0, 1, 2))
const facesColors = [
0xFFFF00, // yellow
0xFF0000, // red
0x0000FF, // blue
0x008000 // green
]
// 36 triangles composing the pyraminx
// numbers are indexes of facesColors 
const pos = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3
]
// vectors of each triangle composing the tetrahedron
const facesVectors = [
[0, 0, -1.5],
[-0.52, 0, -0.6],
[0, 0, -0.48],
[0.52, 0, -0.6],
[-1.04, 0, 0.3],
[-0.52, 0, 0.42],
[0, 0, 0.3],
[0.52, 0, 0.42],
[1.04, 0, 0.3],
[-1.2, -0.16, 0.2],
[-1.04, -0.45, 0.55],
[-0.52, -0.34, 0.62],
[0, -0.45, 0.55],
[0.52, -0.34, 0.62],
[1.04, -0.45, 0.55],
[1.2, -0.16, 0.15],
[-0.6, -0.16, -0.7],
[-0.9, -0.3, -0.1],
[-0.5, -0.5, -0.5]
]
for (let i = 0; i < facesVectors.length; i++) {
material = new THREE.MeshBasicMaterial({ color: facesColors[pos[i]] })
face = new THREE.Mesh(geometry, material)
face.position.set(facesVectors[i][0], facesVectors[i][1], facesVectors[i][2])
// some rotations
if ([0, 1, 2, 3, 4, 5, 6, 7, 8].includes(i)) {
face.rotation.x = -(Math.PI / 2)
}
if ([2, 5, 7, 10, 12, 14].includes(i)) { // 180
face.rotation.z = Math.PI
}
if ([9, 16, 17, 18, 25, 26, 27, 28, 29].includes(i)) {
if (i === 17) {
face.rotation.x = -(1 * Math.PI) / 6
face.rotation.y = -(2 * Math.PI) / 3
face.rotation.z = -(1 * Math.PI) / 6
} else {
face.rotation.x = -Math.PI / 6
face.rotation.y = -2 * Math.PI / 3
face.rotation.z = Math.PI / 6
}
}
if ([15, 22, 23, 24, 31, 32, 33, 34, 35].includes(i)) {
face.rotation.x = -Math.PI / 6
face.rotation.y = 2 * Math.PI / 3
face.rotation.z = -Math.PI / 6
} else if ([10, 11, 12, 13, 14, 19, 20, 21, 30].includes(i)) {
face.rotation.x = Math.PI / 6
}
scene.add(face)
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

四面体的4个角点是:

let s_8_9 = Math.sqrt(8/9), s_2_9 = Math.sqrt(2/9), s_2_3 = Math.sqrt(2/3);
let v = [
new THREE.Vector3(0,0,1),
new THREE.Vector3(s_8_9,0,-1/3),
new THREE.Vector3(-s_2_9,s_2_3,-1/3),
new THREE.Vector3(-s_2_9,-s_2_3,-1/3) 
];

使用THREE.Vector3().lerpVectors计算边上的点:

let pointOnEdge = (pt1, pt2, t) => new THREE.Vector3().lerpVectors(pt1, pt2, t);

以及三角形面的向内偏移点:

let computeOffsetPts = (pts, d) => {
let offsetPts = [];
for (let i = 0; i < pts.length; ++i) {
let va = pointOnEdge(pts[i], pts[(i+1) % 3], d);
let vb = pointOnEdge(pts[i], pts[(i+2) % 3], d); 
offsetPts.push(new THREE.Vector3().lerpVectors(va, vb, 0.5)); 
}
return offsetPts;
}

使用这些点来构造网格。例如:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(-2, 1, 3);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new THREE.OrbitControls(camera, renderer.domElement);
const facesColors = [
0xFFFF00, // yellow
0xFF0000, // red
0x0000FF, // blue
0x008000 // green
]
let s_8_9 = Math.sqrt(8/9), s_2_9 = Math.sqrt(2/9), s_2_3 = Math.sqrt(2/3);
let v = [
new THREE.Vector3(0,0,1),
new THREE.Vector3(s_8_9,0,-1/3),
new THREE.Vector3(-s_2_9,s_2_3,-1/3),
new THREE.Vector3(-s_2_9,-s_2_3,-1/3) 
];
let faces = [[0, 1, 2], [0, 2, 3], [0, 3, 1], [1, 3, 2]]
let pointOnEdge = (pt1, pt2, t) => new THREE.Vector3().lerpVectors(pt1, pt2, t);
let computeOffsetPts = (pts, d) => {
let offsetPts = [];
for (let i = 0; i < pts.length; ++i) {
let va = pointOnEdge(pts[i], pts[(i+1) % 3], d);
let vb = pointOnEdge(pts[i], pts[(i+2) % 3], d); 
offsetPts.push(new THREE.Vector3().lerpVectors(va, vb, 0.5)); 
}
return offsetPts;
}
let newTriangle = (pts, color, d) => {
let innerPts = computeOffsetPts(pts, d);
let material = new THREE.MeshBasicMaterial({ color: color })
let geometry = new THREE.Geometry();
geometry.vertices.push(...innerPts); 
geometry.faces.push(new THREE.Face3(0, 1, 2));
return new THREE.Mesh(geometry, material);
}
const d = 0.05;
for (let i=0; i < 4; ++i ) {

let color = facesColors[i]; 
let pts = [v[faces[i][0]], v[faces[i][1]], v[faces[i][2]]];
let centerPt = new THREE.Vector3().addVectors(pts[0], pts[1]).add(pts[2]).divideScalar(3);
let hexagonPts = [];
for (let j = 0; j < 3; ++j) {
hexagonPts.push(pointOnEdge(pts[j], pts[(j+1) % 3], 1/3), pointOnEdge(pts[j], pts[(j+1) % 3], 2/3));
}

for  (let j = 0; j < 3; ++j) {
let topPts = [pts[j], hexagonPts[j*2], hexagonPts[(j*2+5)%6]];
let face = newTriangle(topPts, color, d);
scene.add(face);
}
for (let j = 0; j < hexagonPts.length; ++j) {
innerPts = [centerPt, hexagonPts[j], hexagonPts[(j+1)%hexagonPts.length]];
let face = newTriangle(innerPts, color, d);
scene.add(face);
}
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
<script src="https://rawcdn.githack.com/mrdoob/three.js/r113/build/three.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/r113/examples/js/controls/OrbitControls.js"></script>

最新更新