第02章:环境搭建与第一个程序
上一章我们从宏观上认识了 OpenCSG.NET。本章开始动手:安装开发环境、把 OpenCSG.NET 引入项目、看懂官方仓库的结构,并亲手写出、运行、导出你的第一个三维零件。
2.1 准备开发环境
2.1.1 安装 .NET SDK
OpenCSG.NET 的核心库目标框架是 netstandard2.0,这意味着几乎任何现代 .NET 都能引用它。但要编译示例、测试、或使用官方解决方案,建议安装较新的 SDK:
- 最低要求:.NET 8 SDK(示例/测试/性能项目均为
net8.0)。 - 推荐:.NET 9 SDK 或更高——因为官方解决方案文件采用了新的
.slnx(XML 格式),只有 .NET 9+ 的dotnetCLI 才能直接构建它。
验证安装:
dotnet --version
dotnet --list-sdks
只要能看到 8.x 或更高版本即可。Windows、macOS、Linux 三平台通用。
2.1.2 选择编辑器
任何支持 C# 的编辑器都可以:
- Visual Studio 2022(Windows,功能最全)
- Visual Studio Code + C# Dev Kit 扩展(跨平台,轻量)
- JetBrains Rider(跨平台,体验优秀)
2.2 三种引入 OpenCSG.NET 的方式
2.2.1 方式一:NuGet 包(最推荐)
绝大多数使用者应该直接引用 NuGet 上发布的 OpenCSG.NET 包。新建一个控制台项目并添加包:
dotnet new console -n MyCsgApp
cd MyCsgApp
dotnet add package OpenCSG.NET
这会在 MyCsgApp.csproj 中加入一行 PackageReference:
<ItemGroup>
<PackageReference Include="OpenCSG.NET" Version="*" />
</ItemGroup>
提示:
OpenCSG.NET核心库只对System.Text.Json有一个间接依赖,NuGet 会自动帮你还原,无需手动添加。
2.2.2 方式二:项目引用(源码集成)
如果你想跟踪最新源码、调试内核,或做二次开发,可以把 OpenCSG.NET 仓库克隆到本地,然后在你的项目里引用它的 csproj:
<ItemGroup>
<ProjectReference Include="..\OpenCSG.NET\src\OpenCSG.NET\OpenCSG.NET.csproj" />
</ItemGroup>
官方的三个示例/测试项目都是用这种方式引用核心库的。
2.2.3 方式三:直接拷贝源码
因为核心库零第三方依赖(除序列化用到的 System.Text.Json),源码文件也不多,你甚至可以把 src/OpenCSG.NET/*.cs 直接拷进你的工程。这在受限环境(如某些 Unity 场景)里很实用,但一般不推荐,因为难以跟踪上游更新。
2.3 看懂官方仓库结构
克隆官方仓库后,你会看到如下结构。理解它有助于你在需要时快速定位源码、示例和测试。
OpenCSG.NET/
├── OpenCSG.NET.slnx # 解决方案(新版 XML 格式,需 .NET 9+)
├── README.md # 项目说明(英文 + 中文)
├── LICENSE # MIT 许可证
├── OpenJsCad_LICENSE.txt # 上游 csg.js 的许可证(血统致谢)
├── .editorconfig # 代码风格约定
├── .github/workflows/
│ └── nuget-publish.yml # 打 tag 时自动发布到 NuGet 的 CI
├── src/OpenCSG.NET/ # ★ 核心库(本教程主角)
│ ├── OpenCSG.NET.csproj
│ ├── Vector.cs # Vector3D/Vector2D/Matrix4x4/BoundingBox 等数学类型
│ ├── Vertex.cs # 顶点(位置 + 纹理坐标)
│ ├── Plane.cs # 平面与多边形分割
│ ├── Polygon.cs # 凸多边形
│ ├── Solid.cs # ★ 实体:布尔运算、规范化、Tag 系统
│ ├── Solids.cs # ★ 形体工厂:Cube/Sphere/Cylinder + 静态布尔
│ ├── Tree.cs # BSP 树(迭代式)
│ ├── Formats.cs # STL 导出(ASCII + 二进制)
│ ├── CsgNode.cs # 声明式节点 record 定义
│ ├── CsgEvaluator.cs # 声明式树的求值器
│ ├── CsgSerialization.cs # CsgNode 的 JSON 序列化
│ └── Profiles.cs # 参数化型材截面(H 型钢、槽钢等)
├── samples/
│ ├── Runner.Examples/ # 基础用法示例(形体/变换/布尔/杠铃/管道/Wiki 图标)
│ └── Runner.CPurlin/ # 进阶实战:冷弯 C 型钢檩条
├── tests/OpenCSG.NET.Tests/ # NUnit 测试(近似 STL 比对)
└── perf/OpenCSG.NET.PerfTest/ # BenchmarkDotNet 性能基准
核心库一共 13 个 .cs 文件,本教程会把它们逐一讲透。其中 Solid.cs、Solids.cs、Tree.cs 是内核三剑客,CsgNode.cs/CsgEvaluator.cs/CsgSerialization.cs/Profiles.cs 组成声明式层。
2.4 构建与测试官方仓库
如果你克隆了源码,可以先跑通官方的构建与测试,确认环境无误。
使用 .NET 9+ SDK(直接构建整个解决方案):
dotnet build OpenCSG.NET.slnx
dotnet test tests/OpenCSG.NET.Tests/
只有 .NET 8 SDK(单独构建核心库与测试):
dotnet build src/OpenCSG.NET/OpenCSG.NET.csproj -c Release
dotnet test tests/OpenCSG.NET.Tests/
之所以要区分,是因为 .slnx 是 .NET 9 引入的新解决方案格式;而核心库本身是 netstandard2.0,用 .NET 8 SDK 也能单独编译。测试项目是 net8.0,NUnit 驱动,采用”近似比对”策略——把生成的 STL 与 tests/.../Results/ 下的已接受结果对比三角面数、总面积和包围盒(第 14 章详解)。
运行官方示例:
dotnet run --project samples/Runner.Examples
dotnet run --project samples/Runner.CPurlin
Runner.Examples 会在工作目录生成 cube.stl、wiki.stl、CsgExamples.stl 等文件,并尝试用系统默认的 STL 查看器打开(这一步在无 GUI 的服务器上可能失败,忽略即可,文件已经生成)。
2.5 你的第一个程序:一个带通孔的法兰盘
现在从零写一个完整程序。目标是做一个”法兰盘”雏形:一个扁圆柱盘体,中间挖一个大通孔,四周挖四个小螺栓孔。
新建控制台项目并添加包(见 2.2.1),然后把 Program.cs 替换为:
using Csg;
using static Csg.Solids;
// 参数(改这些数字即可生成不同规格的法兰)
double plateRadius = 30; // 盘体半径
double plateHeight = 6; // 盘体厚度
double boreRadius = 10; // 中心通孔半径
double boltRadius = 3; // 螺栓孔半径
double boltCircle = 22; // 螺栓孔分布圆半径
// 1) 盘体:一个扁圆柱。注意 Cylinder 默认沿 Y 轴,这里用 RotateX(90) 把它立起来沿 Z 轴
var plate = Cylinder(r: plateRadius, h: plateHeight, center: true).RotateX(90);
// 2) 中心通孔:一个略高于盘体的圆柱,保证完全穿透
var bore = Cylinder(r: boreRadius, h: plateHeight + 2, center: true).RotateX(90);
// 3) 盘体挖去中心孔
var flange = plate.Subtract(bore);
// 4) 四个螺栓孔:把一个小圆柱绕中心旋转到 4 个方位
for (int i = 0; i < 4; i++)
{
double angleDeg = 90 * i;
double rad = angleDeg * System.Math.PI / 180.0;
double x = boltCircle * System.Math.Cos(rad);
double y = boltCircle * System.Math.Sin(rad);
var hole = Cylinder(r: boltRadius, h: plateHeight + 2, center: true)
.RotateX(90) // 立起来沿 Z
.Translate(x: x, y: y); // 平移到分布圆上
flange = flange.Subtract(hole);
}
// 5) 导出为 ASCII STL
using (var writer = new StreamWriter("flange.stl"))
{
flange.WriteStl("flange", writer);
}
System.Console.WriteLine($"完成,共 {flange.Polygons.Count} 个多边形,已写入 flange.stl");
运行:
dotnet run
你会在项目目录里得到 flange.stl,控制台也会打印出结果实体的多边形数量。用 STL 查看器打开,就能看到一个中心带大孔、四周均布 4 个螺栓孔的法兰盘。
2.5.1 这个程序教会我们什么
尽管很短,这个程序已经涵盖了 OpenCSG.NET 命令式 API 的四大要素:
- 参数化——所有尺寸都是变量,改数字就换规格,这正是”代码即模型”的价值。
- 形体默认沿 Y 轴——
Cylinder沿 Y 轴生成,我们用.RotateX(90)把它转到沿 Z 轴。这是新手最常见的困惑,第 5、7 章会深入。 - 不可变对象 + 链式调用——每个返回
Solid的方法都返回新对象,绝不修改原对象。所以我们能安全地复用同一个”孔模板”再平移到不同位置。 - 布尔运算做减法——
Subtract把孔从盘体里”挖掉”。为保证完全穿透,孔的高度总是比盘体略大(plateHeight + 2),这是 CSG 打孔的经典技巧,避免共面导致的数值问题(第 6 章详解)。
2.6 常见环境问题排查
| 现象 | 原因 | 解决 |
|---|---|---|
dotnet build OpenCSG.NET.slnx 报无法识别 .slnx |
SDK 版本过低 | 升级到 .NET 9+ SDK,或改为单独构建 src/.../OpenCSG.NET.csproj |
找不到 Csg 命名空间 |
未添加包或未 using Csg; |
执行 dotnet add package OpenCSG.NET,并加上 using Csg; |
Cube(...) 报未定义 |
缺少静态引入 | 加上 using static Csg.Solids; |
| 生成的 STL 打不开 / 面片穿插 | 孔与实体恰好共面 | 让挖孔实体比目标略高/略大,避免精确共面(第 6 章) |
| 运行示例时报无法启动查看器 | 无 GUI 的服务器环境 | 忽略即可,STL 文件已经成功生成 |
2.7 本章小结
- 安装 .NET 8 SDK(构建核心库/测试/示例的最低要求),如需构建
.slnx解决方案则需 .NET 9+。 - 引入 OpenCSG.NET 有三种方式:NuGet 包(推荐)、项目引用(二次开发)、拷贝源码(受限环境)。使用时记得同时
using Csg;和using static Csg.Solids;。 - 官方仓库分为
src(核心库 13 个文件)、samples(示例)、tests(NUnit 近似测试)、perf(基准测试)四大块。 - 我们写出了第一个参数化程序——一个带通孔与螺栓孔的法兰盘,实践了”参数化、Y 轴默认朝向、不可变链式、布尔挖孔”四个关键点。
下一章我们暂时放下 API,深入理解 CSG 与 BSP 树的核心原理——只有理解了内核如何工作,你才能写出稳健、高效、可预测的建模代码。