第05章:基础形体——立方体、球体、圆柱体
CSG 建模的第一步永远是创建基础形体(primitive),再用布尔运算把它们组合起来。OpenCSG.NET 在 Solids 静态类里提供了三种基础形体的工厂方法:Cube(立方体/长方体)、Sphere(球体)、Cylinder(圆柱/圆锥/圆台/扇形柱)。本章逐一讲透它们的每一个重载、选项类、默认朝向与分辨率控制,并澄清新手最常踩的坑。
所有示例都假定你已经引入了命名空间:
using Csg;
using static Csg.Solids; // 关键:静态引入后可直接写 Cube(...) 等
5.1 分辨率:曲面精度的总开关
在讲具体形体前,先理解一个贯穿始终的概念——分辨率(Resolution)。球和圆柱这类曲面形体,在计算机里必须用有限多个平面多边形去逼近。分辨率就是这个逼近的精细程度。Solid 类定义了两个默认值:
public const int DefaultResolution2D = 32; // 二维(暂未在基础形体用到)
public const int DefaultResolution3D = 12; // 三维形体默认分辨率
分辨率的影响是双向的:
- 越高:曲面越光滑、越接近理想形状,但多边形数量越多,布尔运算越慢、内存越大、STL 文件越大。
- 越低:面数少、计算快,但曲面明显”棱角化”。
选择分辨率的经验法则:先用默认值(12)快速迭代验证形状,最终导出时再按需调高(比如 32、48、64)。对于要 3D 打印的小零件,24~48 通常足够;对可视化预览,12 就够。
5.2 Cube:立方体与长方体
Cube 是最简单的形体,永远由 6 个四边形多边形构成,无论尺寸如何。它有 6 个重载,覆盖从”正方体”到”任意长方体”的各种需求。
5.2.1 六个重载一览
// 1) 正方体:边长 size,默认不居中(一个角在原点,向 +X/+Y/+Z 延伸)
Cube(double size = 1, bool center = false);
// 2) 正方体:边长 size,中心放在指定点 center
Cube(double size, Vector3D center);
// 3) 长方体:三个方向尺寸由 Vector3D size 给出,默认不居中
Cube(Vector3D size, bool center = false);
// 4) 长方体:尺寸 size + 指定中心 center
Cube(Vector3D size, Vector3D center);
// 5) 长方体:分别给出 宽/高/深,默认不居中
Cube(double width, double height, double depth, bool center = false);
// 6) 选项对象:CubeOptions { Center, Radius }
Cube(CubeOptions options);
用法示例:
var box1 = Cube(); // 单位立方体,角在原点,对角点 (1,1,1)
var box2 = Cube(size: 2, center: true); // 边长 2、中心在原点,从 (-1,-1,-1) 到 (1,1,1)
var box3 = Cube(new Vector3D(4, 2, 1)); // 4×2×1 长方体,角在原点
var box4 = Cube(width: 10, height: 5, depth: 3, center: true); // 10×5×3,居中
var box5 = Cube(size: 1, center: new Vector3D(5, 0, 0)); // 中心放在 (5,0,0)
5.2.2 center 的语义
center 参数是大多数形体的通用约定,务必理解清楚:
center: false(Cube 默认):形体的一个角落在原点,向正方向延伸。Cube(2)占据[0,2]×[0,2]×[0,2]。center: true:形体的几何中心落在原点,对称分布。Cube(2, center: true)占据[-1,1]³。
一个重要差异:
Cube默认center: false(角在原点),而下一节的Sphere默认center: true(中心在原点)。这是历史约定,容易混淆——写代码时最好显式写出center:,避免误会。
5.2.3 CubeOptions 与底层表示
public class CubeOptions
{
public Vector3D Center; // 中心
public Vector3D Radius = new Vector3D(1, 1, 1); // 三个方向的"半尺寸"
}
注意底层用的是 Radius(半尺寸) 而非全尺寸——所以 Cube(size: 2) 换算成 Radius = (1,1,1)。所有便捷重载最终都会把参数转成 CubeOptions。另外,负尺寸没有意义,工厂内部会对 Radius 取绝对值;若任一方向尺寸为 0,则返回空实体(new Solid())。
5.3 Sphere:球体
Sphere 生成一个球。它用两层循环沿经度、纬度铺设多边形,多边形数量随分辨率增长。默认分辨率 12 时,球约有 72 个多边形。
5.3.1 三个重载
// 1) 半径 r,默认居中(中心在原点)
Sphere(double r = 1, bool center = true);
// 2) 半径 r,中心放在指定点
Sphere(double r, Vector3D center);
// 3) 选项对象:可控制分辨率
Sphere(SphereOptions options);
用法:
var s1 = Sphere(); // 半径 1,中心在原点
var s2 = Sphere(r: 5); // 半径 5,中心在原点(注意默认就 center: true)
var s3 = Sphere(r: 2, center: new Vector3D(10, 0, 0)); // 半径 2,中心在 (10,0,0)
// 高分辨率球(导出前提高精度)
var smooth = Sphere(new SphereOptions { Radius = 3, Resolution = 48 });
5.3.2 SphereOptions
public class SphereOptions
{
public Vector3D XAxis = new Vector3D(1, 0, 0);
public Vector3D YAxis = new Vector3D(0, -1, 0);
public Vector3D ZAxis = new Vector3D(0, 0, 1);
public Vector3D Center;
public double Radius = 1;
public int Resolution = Solid.DefaultResolution3D; // 默认 12
}
XAxis/YAxis/ZAxis 定义了球体的局部坐标系,一般无需改动(改动可生成椭球或调整极点朝向,属高级用法)。日常你只需要关心 Radius、Center、Resolution 三个字段。半径为 0 时返回空实体;分辨率小于 4 时会被强制提升到 4。
5.3.3 分辨率与面数直觉
| 分辨率 | 视觉效果 | 大致面数 | 典型用途 |
|---|---|---|---|
| 12(默认) | 明显棱角 | ~72 | 快速迭代、预览 |
| 24 | 较光滑 | 更多 | 一般导出 |
| 48 | 光滑 | 很多 | 高质量 3D 打印 |
布尔运算要用高分辨率球时务必谨慎——两个 48 分辨率的球做布尔,面数和计算量都远超默认,第 14 章会给出性能数据。
5.4 Cylinder:圆柱、圆锥、圆台与扇形柱
Cylinder 是三种形体里最强大也最容易踩坑的一个。表面上它是”圆柱”,但通过选项类它还能生成圆锥、圆台(截头锥)、扇形柱(不满一圈)。默认分辨率 12 时圆柱约 36 个多边形。
5.4.1 简易重载:默认沿 Y 轴!
Cylinder(double r, double h, bool center = false);
var cyl = Cylinder(r: 1, h: 5); // 半径 1、高 5,从 y=0 到 y=5
var cylC = Cylinder(r: 1, h: 5, center: true); // 居中,从 y=-2.5 到 y=2.5
这里是新手最大的坑:简易 Cylinder(r, h) 生成的圆柱沿 Y 轴方向!源码里:
var start = center ? new Vector3D(0, -h/2, 0) : new Vector3D(0, 0, 0);
var end = center ? new Vector3D(0, h/2, 0) : new Vector3D(0, h, 0);
也就是说,它的轴线是 Y 轴,不是很多人预期的 Z 轴。如果你想要”竖直站立”(沿 Z 轴)的圆柱,需要额外旋转:
var standing = Cylinder(r: 1, h: 5, center: true).RotateX(90); // 转到沿 Z 轴
第 2 章的法兰盘、第 7 章的变换都会反复用到这个技巧。或者,直接用下面的 CylinderOptions 精确指定 Start/End,就能让圆柱指向任意方向,无需事后旋转。
5.4.2 CylinderOptions:全能形态
public class CylinderOptions
{
public Vector3D Start; // 起点(底面圆心)
public Vector3D End; // 终点(顶面圆心)
public double RadiusStart = 1; // 起点半径
public double RadiusEnd = 1; // 终点半径
public double SectorAngle = 360; // 扇形角度(<360 生成"缺一块"的柱)
public int Resolution = Solid.DefaultResolution3D;
}
这个选项类把”圆柱”泛化成了”广义锥台”。Start/End 是轴线两端的圆心——它们决定了圆柱的位置和朝向,你可以让轴指向任意方向:
// 一根沿 X 轴、从原点到 (10,0,0) 的圆柱
var alongX = Cylinder(new CylinderOptions {
Start = new Vector3D(0, 0, 0),
End = new Vector3D(10, 0, 0),
RadiusStart = 1, RadiusEnd = 1
});
5.4.3 用 Cylinder 做圆锥与圆台
只要让 RadiusStart 与 RadiusEnd 不相等,就得到锥台;让其中一个为 0,就得到真正的圆锥:
// 圆台(截头圆锥):底半径 3,顶半径 1
var frustum = Cylinder(new CylinderOptions {
Start = new Vector3D(0, 0, 0), End = new Vector3D(0, 5, 0),
RadiusStart = 3, RadiusEnd = 1
});
// 圆锥:顶端收成一点
var cone = Cylinder(new CylinderOptions {
Start = new Vector3D(0, 0, 0), End = new Vector3D(0, 5, 0),
RadiusStart = 2, RadiusEnd = 0
});
划重点:这是本库生成圆锥的正确方式。第 9 章会讲到,声明式 API 里虽然定义了
ConeNode,但求值器目前会对它抛出”Cone not yet supported”异常——所以无论命令式还是声明式,需要圆锥时都应该用半径收为 0 的Cylinder来实现。
5.4.4 扇形柱:SectorAngle
SectorAngle 小于 360 时,生成一个”没转满一圈”的扇形柱(像切掉一块的蛋糕):
// 270° 的扇形柱(缺 90°)
var sector = Cylinder(new CylinderOptions {
Start = new Vector3D(0, 0, 0), End = new Vector3D(0, 3, 0),
RadiusStart = 2, RadiusEnd = 2, SectorAngle = 270
});
内部会为扇形的两个”切面”额外补上封闭多边形,保证实体水密。
5.4.5 边界情况
Start与End相等(高度为 0)→ 返回空实体。RadiusStart与RadiusEnd都为 0 → 返回空实体。- 半径为负 → 内部取绝对值。
SectorAngle大于 360 → 对 360 取模。
5.5 形体默认朝向与居中总表
把三种形体的默认行为汇总成一张表,随时查阅:
| 形体 | 默认 center | 默认位置 | 默认朝向 | 默认面数(res 12) | 备注 |
|---|---|---|---|---|---|
| Cube | false | 角在原点 | 轴对齐 | 6 | 长方体用 Vector3D size 或 w/h/d 重载 |
| Sphere | true | 中心在原点 | — | ~72 | 半径默认 1 |
| Cylinder | false | 底在原点 | 沿 Y 轴 | ~36 | Options 可指定任意 Start/End,做锥台/扇形 |
记住两条最容易忘的:Cube 默认不居中、Sphere 默认居中;Cylinder 默认沿 Y 轴。
5.6 组合示例:一支哑铃
用三种形体的知识,复刻官方示例里的”哑铃”——一根细杆两端各一个球:
var bar = Cylinder(r: 0.025, h: 2.1, center: true); // 细杆,沿 Y 轴、居中
var weight = Sphere(r: 0.2, center: true); // 配重球
// 复用同一个 weight,平移到杆的两端(不可变对象,安全复用)
var barbell = Union(
bar,
weight.Translate(y: -0.9),
weight.Translate(y: +0.9)
);
这里 bar 沿 Y 轴,两个球沿 Y 平移到两端,恰好构成一支哑铃。注意我们复用了同一个 weight 对象两次——因为所有变换都返回新实体,原 weight 不受影响(第 4 章的不可变性)。布尔运算 Union 会在下一章详解。
5.7 本章小结
- 分辨率是曲面精度的总开关,默认
DefaultResolution3D = 12;先用默认值迭代,导出时再调高。 - Cube 恒为 6 个多边形,6 个重载覆盖正方体/长方体,底层用”半尺寸 Radius”,默认不居中。
- Sphere 默认分辨率 12(约 72 面),默认居中,
SphereOptions可调分辨率。 - Cylinder 简易重载默认沿 Y 轴、不居中(新手最大的坑);
CylinderOptions通过Start/End指定任意朝向,通过不等半径做圆锥/圆台,通过SectorAngle做扇形柱。 - 需要圆锥时,用
RadiusEnd = 0的Cylinder——这是本库唯一可用的方式(ConeNode尚未实现)。 - 三种形体的默认 center/朝向差异务必牢记,写代码时尽量显式指定
center:。
下一章进入 CSG 的灵魂——布尔运算:如何用并集、差集、交集把这些基础形体拼成任意复杂的零件,以及背后的原点居中修复与常见陷阱。