本文档详细解析 cgaprocessor.js 中 roofPyramid 函数的实现。该函数用于生成金字塔屋顶(Pyramid Roof)——所有坡面汇聚到顶部一点的锥形屋顶。
roofPyramid 生成的是从 footprint 多边形各边向中心上方一点(顶点/apex)收敛的屋顶。它是 roofHip 的退化形式(当建筑轮廓接近正方形时,hip roof 会退化为 pyramid)。
function func_roofPyramid(processor, angle) { var top = processor.top; top.computeBoundingBox(); var c = top.boundingBox.getCenter(); var size = processor.size(); var h = Math.tan(THREE.Math.degToRad(angle || 30)) * Math.min(size.x || 1, size.z || 1) / 2; var g = processor.create(); var apex = new THREE.Vector3(c.x, c.y+h, c.z); top.vertices.forEach((v,i) => { var n = (i+1) % top.vertices.length; var l = g.vertices.length; g.vertices.push(v.clone(), top.vertices[n].clone(), apex.clone()); g.faces.push(new THREE.Face3(l, l+1, l+2)); }); g.mergeVertices(); processor.update(g); }
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
angle | Number | 30 | 屋顶坡度角(度),决定 apex 的高度 |
top.computeBoundingBox();
var c = top.boundingBox.getCenter();
获取当前 footprint 几何体的包围盒中心,作为金字塔顶点的水平投影位置。
var h = Math.tan(THREE.Math.degToRad(angle || 30)) * Math.min(size.x || 1, size.z || 1) / 2;
apex 的高度由坡角和 footprint 的最小跨度决定:
size.x 和 size.z 的较小值作为基准跨度var apex = new THREE.Vector3(c.x, c.y+h, c.z);
在包围盒中心正上方 h 距离处创建锥顶。
top.vertices.forEach((v,i) => {
var n = (i+1) % top.vertices.length;
var l = g.vertices.length;
g.vertices.push(v.clone(), top.vertices[n].clone(), apex.clone());
g.faces.push(new THREE.Face3(l, l+1, l+2));
});
遍历 footprint 的每个顶点,将相邻两顶点与 apex 构成三角形:
i 个顶点,取下一个顶点 (i+1) % Nvᵢ、vᵢ₊₁ 和 apex 三个顶点加入新几何体Face3(l, l+1, l+2)g.mergeVertices(); processor.update(g);
mergeVertices():合并重复顶点(apex 被多次添加,会被合并为单一顶点)processor.update(g):用新几何体替换当前栈顶| 特性 | 说明 |
|---|---|
| 顶点数量 | footprint 顶点数 + 1(apex) |
| 面片数量 | 等于 footprint 的边数(每条边对应一个三角形坡面) |
| 高度计算 | 基于 footprint 最小跨度,确保各坡面角度一致 |
| 退化处理 | roofHip 在 footprint 接近正方形时会调用本函数 |
对于 N 边形的 footprint:
top.vertices 已经按顺序排列成多边形环。如果顶点无序,生成的面片会出现交叉或翻转。
在 func_roofHip 中有两处退化判断:
func_roofPyramid这说明 roofPyramid 是 roofHip 在正方形/近正方形 footprint 下的特例。
// 默认 30 度金字塔顶 Roof --> roofPyramid() // 45 度尖顶 Roof --> roofPyramid(45) // 方形塔楼顶部 Tower --> extrude(5) comp(f) { top: Pyramid } Pyramid --> roofPyramid(60)
function func_roofPyramid(processor, angle) { var top = processor.top; top.computeBoundingBox(); var c = top.boundingBox.getCenter(); var size = processor.size(); var h = Math.tan(THREE.Math.degToRad(angle || 30)) * Math.min(size.x || 1, size.z || 1) / 2; var g = processor.create(); var apex = new THREE.Vector3(c.x, c.y+h, c.z); top.vertices.forEach((v,i) => { var n = (i+1) % top.vertices.length; var l = g.vertices.length; g.vertices.push(v.clone(), top.vertices[n].clone(), apex.clone()); g.faces.push(new THREE.Face3(l, l+1, l+2)); }); g.mergeVertices(); processor.update(g); }
cgaprocessor.js 第 1167~1183 行源码分析。生成时间:2026-05-28。