roofPyramid 函数实现详解

本文档详细解析 cgaprocessor.jsroofPyramid 函数的实现。该函数用于生成金字塔屋顶(Pyramid Roof)——所有坡面汇聚到顶部一点的锥形屋顶。

一、函数定位

roofPyramid 生成的是从 footprint 多边形各边向中心上方一点(顶点/apex)收敛的屋顶。它是 roofHip 的退化形式(当建筑轮廓接近正方形时,hip roof 会退化为 pyramid)。

二、源码实现(第 1167~1183 行)

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);
}

三、参数说明

参数类型默认值说明
angleNumber30屋顶坡度角(度),决定 apex 的高度

四、算法步骤详解

Step 1: 计算 bounding box 和中心点

top.computeBoundingBox();
var c = top.boundingBox.getCenter();

获取当前 footprint 几何体的包围盒中心,作为金字塔顶点的水平投影位置。

Step 2: 计算顶点高度

var h = Math.tan(THREE.Math.degToRad(angle || 30)) * Math.min(size.x || 1, size.z || 1) / 2;

apex 的高度由坡角和 footprint 的最小跨度决定:

为什么用 min/2? 金字塔顶点位于中心,到各边的距离不同。使用最小跨度的一半作为基准,可以确保所有坡面的坡度角至少达到指定值,避免因 footprint 过长导致某些坡面过于平缓。

Step 3: 创建 apex 顶点

var apex = new THREE.Vector3(c.x, c.y+h, c.z);

在包围盒中心正上方 h 距离处创建锥顶

Step 4: 为每条边生成三角形面

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 构成三角形

  1. 对第 i 个顶点,取下一个顶点 (i+1) % N
  2. vᵢvᵢ₊₁apex 三个顶点加入新几何体
  3. 创建一个三角形面 Face3(l, l+1, l+2)
Apex Face 0 Face 1 Face 2 Face 3 v₀ v₁ v₂ v₃ 金字塔顶在中心上方,每边构成一个三角形坡面

Step 5: 合并顶点并更新

g.mergeVertices();
processor.update(g);

五、关键特性

特性说明
顶点数量 footprint 顶点数 + 1(apex)
面片数量等于 footprint 的边数(每条边对应一个三角形坡面)
高度计算基于 footprint 最小跨度,确保各坡面角度一致
退化处理roofHip 在 footprint 接近正方形时会调用本函数

六、几何结构示意

对于 N 边形的 footprint:

注意:此函数假设 top.vertices 已经按顺序排列成多边形环。如果顶点无序,生成的面片会出现交叉或翻转。

七、与 roofHip 的关系

func_roofHip 中有两处退化判断:

  1. 当 footprint 长短边比例 > 0.85(接近正方形)时,直接调用 func_roofPyramid
  2. 当计算的 hipRun 超过短边一半时,也退化为 pyramid

这说明 roofPyramidroofHip 在正方形/近正方形 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。