📋 实现概述
本次扩展在 src/cgaprocessor.js 中通过 register_func() 机制注册了 47 个 CityEngine 官方操作函数。所有函数均遵循现有引擎架构:
- processor.top — 当前 Three.js Geometry 对象
- processor.create() — 创建新几何体并继承属性(pivot、attrs)
- processor.update(g) — 替换栈顶几何体
- processor.stack.push/pop + applyOperations() — 子作用域执行(Body 操作)
同时修复了 src/cgaparser.js 中的模块路径问题(require("cga") → require("./cga")),使 Node.js 直接运行测试成为可能。
🧊 几何创建函数 4
primitiveQuad(w, h)
Geometry创建四边形平面,默认适配当前 scope 的 x/z 尺寸。
w 宽度, h 高度THREE.PlaneGeometry → 绕 X 轴旋转 -90° → 平移到 boundingBox 中心。// 创建一个 2x3 的平面
Lot --> primitiveQuad(2, 3) color("red")
primitiveDisk(r, seg)
Geometry创建圆盘(圆形平面),默认适配 scope 的 x/z 最大值的一半作为半径。
r 半径, seg 分段数(默认 32)THREE.CircleGeometry → 绕 X 轴旋转 -90° → 平移到中心。// 创建半径为 1.5 的圆盘
Lot --> primitiveDisk(1.5, 32)
primitiveCone(r, h, seg)
Geometry创建圆锥体,默认适配当前 scope。
r 底面半径, h 高度, seg 分段数(默认 16)THREE.ConeGeometry → 平移使底面位于 y=0。// 创建圆锥
Lot --> primitiveCone(1, 2, 16) color("blue")
primitivePyramid(w, d, h)
Geometry创建四棱锥(金字塔),手动构建 5 个顶点(底面 4 点 + 顶点)和 6 个面。
w 底面宽, d 底面深, h 高度THREE.Geometry 顶点与面索引 → mergeVertices()。// 创建金字塔
Lot --> primitivePyramid(2, 2, 1.5)
🏠 屋顶操作 5
roofHip(height)
Roof四坡屋顶。提取 footprint 边界,生成向内收缩的顶部平顶 + 斜面。
height 屋顶高度// 四坡屋顶
Lot --> extrude(2) roofHip(1)
roofGable(height)
Roof人字屋顶(三角顶)。沿 footprint 的 X 轴方向创建脊线。
height 屋顶高度// 人字屋顶
Lot --> primitiveCube(3, 2, 3) roofGable(1.2)
roofPyramid(height)
Roof金字塔屋顶。所有边界顶点连接到 footprint 中心上方的顶点。
height 屋顶高度// 金字塔屋顶(适合方形 base)
Lot --> primitiveCube(2, 2, 2) roofPyramid(1.5)
roofRidge(height, angle)
Roof脊线屋顶。在 footprint 中心上方创建一条脊线,两侧坡度对称。
height 高度, angle 坡度角(可选,保留参数)// 脊线屋顶
Lot --> primitiveCube(4, 2, 3) roofRidge(1)
roofShed(height, angle)
Roof单坡屋顶。中心一点高,所有边界连接到中心顶点形成单一坡度。
height 高度, angle 坡度角(可选)// 单坡屋顶
Lot --> primitiveCube(2, 2, 2) roofShed(1)
🔧 几何修改操作 17
offset(distance)
Modify将所有面沿法线方向平移指定距离,生成"外壳"几何体。
face.normal 偏移 distance → 重建面索引。envelope(angle)
Modify锥化/包络操作。根据角度参数将上部顶点向中心收缩。
scale = 1 - t * (1 - factor))。cleanupGeometry()
Modify清理几何体,合并重合顶点。
geometry.mergeVertices()。convexify()
Modify凸化几何体。用当前 bounding box 替代原几何体。
BoxGeometry 替换原几何体。deleteHoles()
Modify删除内部孔洞面。识别共享边数 > 2 的内部面并移除。
reverseNormals()
Modify反转所有面的法线方向。
a 和 b 顶点索引。setNormals()
Modify重新计算面法线和顶点法线。
computeFaceNormals() + computeVertexNormals()。softenNormals()
Modify软化法线,使光照过渡平滑。
computeVertexNormals()(共享顶点法线取平均)。rectify()
Modify矫正几何体为轴对齐包围盒。
BoxGeometry 替换。reduceGeometry(percentage)
Modify简化几何体,按百分比减少面数。
faces.pop() 直到达标。resetGeometry()
Modify重置几何体为当前 scope 的轴对齐包围盒。
trim()
Modify删除面积过小的退化面。
tag(tagName)
Modify为当前几何体添加标签。
geometry.attrs.tags 数组中追加标签名。deleteTags()
Modify删除当前几何体的所有标签。
geometry.attrs.tags 数组。mirror(axis)
Modify沿指定轴镜像几何体。
geometry.scale(-1, 1, 1) 等。footprint()
Modify提取当前几何体在 XZ 平面的投影(bounding box 底面)。
📐 Scope / 变换操作 7
alignScopeToAxes(axis)
Scope将 scope 的旋转重置为与世界坐标轴对齐。
pivotRotate = new Quaternion()(单位四元数)→ 更新变换矩阵。alignScopeToGeometry()
Scope将 scope 对齐到第一个面的法线方向。
pivotRotate。alignScopeToGeometryBBox()
Scope将 scope 的 pivot 设为几何体 bounding box 中心,旋转重置。
pivot = boundingBox.getCenter() + pivotRotate = 单位四元数。rotateScope(x, y, z)
Scope绕各轴旋转 scope(角度制)。
Quaternion.setFromEuler → premultiply 到现有 pivotRotate。setPivot(x, y, z)
Scope设置 pivot 点到指定世界坐标。
pivot = new Vector3(x, y, z) → 更新变换矩阵。mirrorScope(axis)
Scope沿指定轴镜像 scope 变换矩阵。
pivotTransform 调用 scale(-1, 1, 1) 等 → 重算逆矩阵。NIL
Scope空操作,不产生任何几何输出。
stack.pop(); stack.push(null)。在 applyRule 中 null 被识别为 NIL 规则。🎨 材质 / UV 操作 7
texture(name)
Material设置纹理名称。
set(material.texture, name) → 存入 attrs.material.texture。setupProjection(mode, axes)
Material设置纹理投影模式和轴向。
attrs.material.projectionMode 和 projectionAxes。translateUV(x, y)
UV平移 UV 坐标。
attrs.uv.x / .y。scaleUV(x, y)
UV缩放 UV 坐标。
attrs.uv.sx / .sy。rotateUV(angle)
UV旋转 UV 坐标。
attrs.uv.rot。normalizeUV()
UV重置 UV 到标准状态(无平移、无缩放、无旋转)。
attrs.uv = {x:0, y:0, sx:1, sy:1, rot:0}。tileUV(x, y)
UVUV 平铺(同 scaleUV 的语义包装)。
attrs.uv.sx / .sy。🔗 插入 / 规则调用 2
i(shapeName)
Insert插入(调用)指定名称的规则。同 CityEngine 的 i() 操作。
processor.rules[shapeName] → 调用 applyRule()。insert(shapeName)
Inserti() 的完整名称别名。
i() 共用 func_insert 实现。📦 带 Body 的操作 6
setback(distance) { ... }
Body将 footprint 向内收缩指定距离后执行 body。
distance → 创建矩形 footprint → push/pop 栈执行 body。Lot --> setback(0.5) { color("red") }
shapeL(fw, lw) { ... }
Body将 footprint 分割为 L 形(前条 + 左条)。
Lot --> shapeL(1, 1) { front: color("red") | left: color("blue") }
shapeU(fw, lw, rw) { ... }
Body将 footprint 分割为 U 形(前条 + 左条 + 右条)。
shapeO(fw, lw, rw, bw) { ... }
Body将 footprint 分割为 O 形(四边条状)。
innerRectangle { ... }
Body在 footprint 内部生成一个内缩矩形并执行 body。
splitAndSetbackPerimeter(offset) { length : depth : ops | ... } { remainder : ops }
Body沿多边形周长分割并退缩。根据指定的 lengths 将面的周长分割成多个部分,然后对每个部分进行退缩。
Lot --> splitAndSetbackPerimeter(6)
{ 40 : 5 : Yellow |
20 : 10 : Blue }
{ remainder : Grey }
scatter(count) { ... }
Body在 footprint 范围内随机散布指定数量的小几何体并执行 body。
count 次 → 随机生成 X/Z 位置 → 创建小三角面 → 执行 body。Lot --> scatter(10) { primitiveCone(0.1, 0.2) }
📊 完整函数清单
| 函数名 | 类别 | 参数 | Body | 实现文件 |
|---|---|---|---|---|
primitiveQuad | 几何创建 | 0~2 (w, h) | 否 | cgaprocessor.js |
primitiveDisk | 几何创建 | 0~2 (r, seg) | 否 | cgaprocessor.js |
primitiveCone | 几何创建 | 0~3 (r, h, seg) | 否 | cgaprocessor.js |
primitivePyramid | 几何创建 | 0~3 (w, d, h) | 否 | cgaprocessor.js |
roofHip | 屋顶 | 1 (height) | 否 | cgaprocessor.js |
roofGable | 屋顶 | 1 (height) | 否 | cgaprocessor.js |
roofPyramid | 屋顶 | 1 (height) | 否 | cgaprocessor.js |
roofRidge | 屋顶 | 1~2 (height, angle) | 否 | cgaprocessor.js |
roofShed | 屋顶 | 1~2 (height, angle) | 否 | cgaprocessor.js |
offset | 几何修改 | 1 (distance) | 否 | cgaprocessor.js |
envelope | 几何修改 | 0~1 (angle) | 否 | cgaprocessor.js |
cleanupGeometry | 几何修改 | 0 | 否 | cgaprocessor.js |
convexify | 几何修改 | 0 | 否 | cgaprocessor.js |
deleteHoles | 几何修改 | 0 | 否 | cgaprocessor.js |
reverseNormals | 几何修改 | 0 | 否 | cgaprocessor.js |
setNormals | 几何修改 | 0 | 否 | cgaprocessor.js |
softenNormals | 几何修改 | 0 | 否 | cgaprocessor.js |
rectify | 几何修改 | 0 | 否 | cgaprocessor.js |
reduceGeometry | 几何修改 | 0~1 (percentage) | 否 | cgaprocessor.js |
resetGeometry | 几何修改 | 0 | 否 | cgaprocessor.js |
trim | 几何修改 | 0 | 否 | cgaprocessor.js |
tag | 几何修改 | 1 (tagName) | 否 | cgaprocessor.js |
deleteTags | 几何修改 | 0 | 否 | cgaprocessor.js |
mirror | 几何修改 | 1 (axis) | 否 | cgaprocessor.js |
footprint | 几何修改 | 0 | 否 | cgaprocessor.js |
alignScopeToAxes | Scope | 0~1 (axis) | 否 | cgaprocessor.js |
alignScopeToGeometry | Scope | 0 | 否 | cgaprocessor.js |
alignScopeToGeometryBBox | Scope | 0 | 否 | cgaprocessor.js |
rotateScope | Scope | 3 (x, y, z) | 否 | cgaprocessor.js |
setPivot | Scope | 3 (x, y, z) | 否 | cgaprocessor.js |
mirrorScope | Scope | 1 (axis) | 否 | cgaprocessor.js |
NIL | Scope | 0 | 否 | cgaprocessor.js |
texture | 材质 | 1 (name) | 否 | cgaprocessor.js |
setupProjection | 材质 | 1~2 (mode, axes) | 否 | cgaprocessor.js |
translateUV | UV | 1~2 (x, y) | 否 | cgaprocessor.js |
scaleUV | UV | 1~2 (x, y) | 否 | cgaprocessor.js |
rotateUV | UV | 1 (angle) | 否 | cgaprocessor.js |
normalizeUV | UV | 0 | 否 | cgaprocessor.js |
tileUV | UV | 1~2 (x, y) | 否 | cgaprocessor.js |
i / insert | 插入 | 1 (shapeName) | 否 | cgaprocessor.js |
setback | Body | 1 (distance) | 是 | cgaprocessor.js |
shapeL | Body | 2 (fw, lw) | 是 | cgaprocessor.js |
shapeU | Body | 3 (fw, lw, rw) | 是 | cgaprocessor.js |
shapeO | Body | 4 (fw, lw, rw, bw) | 是 | cgaprocessor.js |
innerRectangle | Body | 0 | 是 | cgaprocessor.js |
splitAndSetbackPerimeter | Body | 1~3 (offset, firstEdgeIndex, selectedEdgesMask) | 是 | cgaprocessor.js |
scatter | Body | 1 (count) | 是 | cgaprocessor.js |