znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第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 定义了球体的局部坐标系,一般无需改动(改动可生成椭球或调整极点朝向,属高级用法)。日常你只需要关心 RadiusCenterResolution 三个字段。半径为 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 做圆锥与圆台

只要让 RadiusStartRadiusEnd 不相等,就得到锥台;让其中一个为 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 边界情况

  • StartEnd 相等(高度为 0)→ 返回空实体
  • RadiusStartRadiusEnd 都为 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 = 0Cylinder——这是本库唯一可用的方式(ConeNode 尚未实现)。
  • 三种形体的默认 center/朝向差异务必牢记,写代码时尽量显式指定 center:

下一章进入 CSG 的灵魂——布尔运算:如何用并集、差集、交集把这些基础形体拼成任意复杂的零件,以及背后的原点居中修复与常见陷阱。


← 上一章 目录 下一章 →