znlgis 博客

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

第13章:实战案例——冷弯 C 型钢檩条建模

理论学完,本章把前面所有知识揉进一个完整的工程实战:用命令式 API 建出一根冷弯薄壁 C 型钢檩条(Cold-formed C-purlin)——带卷边、圆角折弯、腰形冲孔、端部缺口的真实构件。这个案例直接取自 OpenCSG.NET 官方仓库的 samples/Runner.CPurlin 示例,是展示这个库工程能力的旗舰 demo。跟着走一遍,你会真正体会到”用代码造零件”的思维方式。

13.1 认识 C 型钢檩条

檩条(purlin) 是屋面/墙面结构里承托压型钢板的次级构件。冷弯 C 型钢是把一条薄钢板在常温下辊压折弯成”C”字截面:

  • 腹板(web):C 的竖直背板。
  • 翼缘(flange):上下两条水平边。
  • 卷边/唇边(lip):翼缘末端向内折的小边,用来提高抗屈曲能力。
  • 折弯圆角(radius):冷弯工艺决定了每个转角都是圆角而非尖角。

真实的檩条上还会冲孔(穿螺栓/管线,常用抗滑移的腰形孔)并在端部开缺口以便搭接。我们要把这些全建出来。

13.2 参数化:先定义尺寸

好的工程模型都是参数驱动的——把所有关键尺寸提取成变量,改一个数就是一个新规格。示例开头就是一组参数:

using Csg;
using static Csg.Solids;

var web = 152.0;        // 腹板高度
var flange = 64.0;      // 翼缘宽度
var lip = 18.5;         // 卷边长度
var thickness = 2.4;    // 板厚
var height = 1000.0;    // 檩条长度(沿构件轴向)
var r = 5.0;            // 折弯内圆角半径

var holeSize1 = 18.0;   // 孔短径
var holeSize2 = 22.0;   // 孔长径(> 短径 → 腰形孔)

var webCenter = 60.0;   // 腹板双孔的中心间距
var punchingsWeb    = new List<double>() { 35 };   // 腹板孔沿长度的位置
var punchingsFlange = new List<double>() { 70 };   // 翼缘孔位置
var punchingsCenter = new List<double>() { 105 };  // 腹板中心孔位置

一个小工具函数简化向量书写(示例把它放在文件末尾,用局部函数):

static Vector3D V3D(double x, double y, double z) => new(x, y, z);

建模思路:C 型钢的截面沿 Z 轴拉伸 height。我们不用第 10 章的 ExtrudeNode(它处理不了圆角和局部冲孔),而是把截面拆成若干”薄板长条” ——每条板都是一个薄 Cube,长度方向(Z)等于 height——再加上圆角转角,最后并成一体、局部挖孔。这是命令式布尔的用武之地。

13.3 腹板与冲孔

先建腹板:一块thickness(X 向)、高 web - 2r(Y 向,两端留出圆角)、长 height(Z 向) 的薄板,摆在截面左侧(X 负方向):

var sweb = Cube(
    center: V3D(-flange / 2 + thickness / 2, 0, 0),
    size:   V3D(thickness, web - r * 2, height));

然后在腹板上冲孔。示例演示了两组孔——一组是上下对称的双孔(间距 webCenter),一组是中心单孔——都用 Subtract 从腹板上挖掉:

foreach (var p in punchingsWeb)     // 上下对称双孔
{
    var holeTop    = Punching(p, onWeb: true).Translate(-flange / 2, +webCenter / 2, 0);
    var holeBottom = Punching(p, onWeb: true).Translate(-flange / 2, -webCenter / 2, 0);
    sweb = sweb.Subtract(holeTop, holeBottom);
}

foreach (var p in punchingsCenter)  // 中心单孔
{
    var holeCenter = Punching(p, onWeb: true).Translate(-flange / 2, 0, 0);
    sweb = sweb.Subtract(holeCenter);
}

Subtract 可以一次减多个(params),所以 sweb.Subtract(holeTop, holeBottom) 一步挖两个孔。

13.4 腰形孔(obround)的构造

Punching 局部函数是这个案例的一个亮点——它构造一个腰形孔(也叫长圆孔/腰圆孔):当长径 holeSize2 大于短径 holeSize1 时,孔不是简单的圆柱,而是”两个半圆 + 中间矩形”的胶囊柱:

Solid Punching(double z, bool onWeb = false)
{
    // 基础圆柱(贯穿板厚,取 4 倍厚以保证完全穿透)
    var punching = Cylinder(holeSize1 / 2, thickness * 4, true);

    if (holeSize2 > holeSize1)   // 长径更大 → 做成腰形
    {
        var diff = holeSize2 - holeSize1;
        punching = Union(
            punching.Translate(z: -diff / 2),   // 一端圆柱
            punching.Translate(z: +diff / 2),   // 另一端圆柱
            Cube(V3D(holeSize1, thickness * 4, diff), center: true)  // 中间连接段
        );
    }

    if (onWeb)
        punching = punching.RotateZ(90);   // 腹板孔转 90° 摆正

    return punching.Translate(0, 0, height / 2 - z);   // 沿长度定位
}

要点:

  • 贯穿保证:钻孔圆柱高度取 thickness * 4,远大于板厚,确保布尔差集完全穿透——留余量是挖孔的实用技巧,避免因共面精度问题在孔口留下薄膜。
  • 腰形 = 两圆柱 ∪ 一方块:这正是第 6 章”并集造复合形状”的典型应用。
  • center: true:所有圆柱/方块都取居中,方便对齐。
  • 方向与定位onWeb 决定是否 RotateZ(90) 摆正,末尾 Translate 把孔移到沿构件长度的指定位置。

13.5 翼缘、卷边与冲孔

上下翼缘是thickness(Y 向)、宽 flange - 2r(X 向) 的水平板,摆在 y = ±(web/2 - thickness/2)

var sflangetop = Cube(
    center: V3D(0, web / 2 - thickness / 2, 0),
    size:   V3D(flange - r * 2, thickness, height));
var sflangebottom = Cube(
    center: V3D(0, -web / 2 + thickness / 2, 0),
    size:   V3D(flange - r * 2, thickness, height));

foreach (var p in punchingsFlange)   // 翼缘上下各冲一孔
{
    sflangetop    = sflangetop.Subtract(Punching(p).Translate(0, +web / 2, 0));
    sflangebottom = sflangebottom.Subtract(Punching(p).Translate(0, -web / 2, 0));
}

卷边是翼缘末端向内折的小竖边,thickness(X 向)、高 lip - r(Y 向),摆在开口侧 x = flange/2 - thickness/2

var sliptop = Cube(
    center: V3D(flange / 2 - thickness / 2, web / 2 - r / 2 - lip / 2, 0),
    size:   V3D(thickness, lip - r, height));
var slipbottom = Cube(
    center: V3D(flange / 2 - thickness / 2, -web / 2 + r / 2 + lip / 2, 0),
    size:   V3D(thickness, lip - r, height));

至此,C 截面的五条直板(腹板、上下翼缘、上下卷边)都齐了。它们之间还差四个圆角转角来连成一体。

13.6 圆角转角:管环 ∩ 象限

冷弯的转角是圆角。Corner(x, y) 用一个巧妙的三步布尔构造一个 90° 的圆角折弯(xy 取 ±1 选择四个象限之一):

Solid Corner(int x, int y)
{
    return Cylinder(r, height, true)                         // ① 外圆柱(半径 r)
        .Subtract(Cylinder(r - thickness, height, true))     // ② 挖去内圆柱 → 得到"管环"(壁厚 thickness)
        .RotateX(90)                                          //   摆正到沿 Z 轴
        .Intersect(Cube(                                      // ③ 与一个象限方块求交 → 只留 1/4 圆弧
            center: V3D(x * r / 2, y * r / 2, 0),
            size:   V3D(r, r, height)))
        .Translate(x * flange / 2 - x * r, y * web / 2 - y * r, 0);  // 移到对应转角
}

拆解:

  1. 外圆柱减内圆柱 = 一个圆管/环(annulus),环壁厚正好是 thickness——这就是折弯处的钢材。
  2. Intersect 一个象限方块:整根圆管是完整 360°,我们只要转角处的那 90° 圆弧。用一个偏置到某象限的方块(x*r/2, y*r/2)与圆管求交,切出四分之一弧。x/y 的正负号决定切哪个象限。
  3. Translate 就位:把这段圆弧移到对应的 web-flange 交角。

四次调用 Corner(-1,+1)Corner(+1,+1)Corner(-1,-1)Corner(+1,-1) 生成四个转角。这个”管环 ∩ 象限”的手法是 CSG 建圆角的经典套路,值得记住。

13.7 总装、定向与端部缺口

把五条直板和四个圆角一次性并集,再做整体定向:

var s = Union(
        sweb, sflangetop, sflangebottom, sliptop, slipbottom,
        Corner(-1, +1), Corner(+1, +1), Corner(-1, -1), Corner(+1, -1))
    .RotateX(90)
    .Translate(0, height / 2, 0)
    .RotateX(90);

末尾的 RotateX(90)TranslateRotateX(90) 是把整根构件从”截面在 XY、长度沿 Z”的建模姿态,翻转/平移到期望的输出姿态。多步刚体变换组合定位,是装配建模的常规操作(回顾第 7 章)。

最后在一端挖一个缺口(notch) ——用一个大立方体做差集,模拟檩条端部为搭接而切掉的一角:

var notch = Cube(web, web, web, true)
    .Translate(0, -web + lip * 1.5, height / 2);
s = s.Subtract(notch);

13.8 双格式导出

成品同时导出 ASCII二进制 两种 STL(回顾第 8 章:TextWriter 走 ASCII,BinaryWriter 走二进制):

using (var fs = File.Create("CPurlin-text.stl"))
using (var sw = new StreamWriter(fs))
    s.WriteStl("CPurlin", sw);          // ASCII

using (var fs = File.Create("CPurlin-bin.stl"))
using (var bw = new BinaryWriter(fs))
    s.WriteStl("CPurlin", bw);          // 二进制(更小)

到此,一根带卷边、圆角、腰形冲孔和端部缺口的冷弯 C 型钢檩条就建好了,可直接送入切片器/查看器或后续 CAE 流程。

13.9 从这个案例学到的建模范式

这个实战浓缩了用 OpenCSG.NET 做工程建模的核心套路,值得提炼成可复用的方法论:

  1. 参数化优先:所有尺寸抽成变量,模型即”参数的函数”,改数即改型号。
  2. 化整为零:复杂截面拆成一组简单薄板长条Cube),而非追求一次成型。
  3. 辅助函数封装重复逻辑PunchingCornerV3D 把重复的构造收进局部函数,主流程清晰如”装配说明书”。
  4. 并集做加法、差集做减法、交集做裁剪:加料用 Union(拼板、拼腰形孔),减料用 Subtract(冲孔、开缺口),取局部用 Intersect(切象限做圆角)。
  5. 留穿透余量:挖孔的刀具比板厚大几倍,规避共面精度问题。
  6. 变换负责定位Translate/Rotate 组合把各零件摆到正确位姿。

掌握这六条,你就能把任何冷弯型材、钣金件、机加工零件”翻译”成 OpenCSG.NET 代码。

13.10 本章小结

  • 完整复现官方 Runner.CPurlin 示例:一根带卷边、圆角折弯、腰形冲孔、端部缺口的冷弯 C 型钢檩条。
  • 截面被拆成五条薄板长条(腹板/上下翼缘/上下卷边)+ 四个圆角转角,并集成一体。
  • 腰形孔 = 两圆柱 ∪ 中间方块;圆角 = (外圆柱 − 内圆柱)∩ 象限方块;冲孔/缺口用差集,刀具留穿透余量。
  • 末尾用 RotateX/Translate 组合定向,导出 ASCII + 二进制两种 STL。
  • 提炼出六条工程建模范式:参数化、化整为零、辅助函数、加/减/裁三类布尔、穿透余量、变换定位。

最后一章我们转向质量与扩展:官方如何用批准测试保证正确性、用 BenchmarkDotNet 量化性能,以及你如何给这个库做二次开发(补全圆锥、添加自定义截面、发布 NuGet)。


← 上一章 目录 下一章 →