znlgis 博客

GIS开发与技术分享

第八章:实体建模系统

8.1 实体建模概述

8.1.1 什么是实体建模

实体建模(Solid Modeling)是三维CAD系统的核心能力,通过对二维轮廓进行拉伸、旋转、放样等操作来创建三维实体对象。LightCAD的实体建模系统位于LightCAD.Core/Elements/Solid/目录下,提供了完整的参数化实体建模功能。

8.1.2 支持的建模操作

操作类型 类名 说明
拉伸 LcExtrude3d 沿方向拉伸轮廓
旋转 LcRevolve3d 绕轴旋转轮廓
放样 LcLoft3d 在多个截面之间过渡
融合 LcBlend3d 在实体边缘创建圆角或倒角
扫掠融合 LcSweptBlend3d 沿路径扫掠的融合体
推拉 LcPushPull3d 直接推拉面
网格 LcMesh3d 网格体表示
布尔运算 LcCombination 并集/交集/差集

8.2 实体基类(LcSolid3d)

8.2.1 数据结构

LcSolid3d是所有实体建模操作的基类,文件大小达26KB,是Core模块中最复杂的类之一:

public abstract class LcSolid3d : LcEntity, IUpdateObject
{
    /// <summary>
    /// 实体的变换矩阵
    /// </summary>
    public Matrix4d Transform { get; set; } = Matrix4d.Identity;

    /// <summary>
    /// 材质
    /// </summary>
    public MaterialInfo Material { get; set; }

    /// <summary>
    /// 三维包围盒
    /// </summary>
    public BoundingBox3d Bounds3d { get; protected set; }

    /// <summary>
    /// 体积
    /// </summary>
    public abstract double Volume { get; }

    /// <summary>
    /// 表面积
    /// </summary>
    public abstract double SurfaceArea { get; }

    /// <summary>
    /// 质心
    /// </summary>
    public abstract Point3d Centroid { get; }

    /// <summary>
    /// 生成三角网格(用于渲染)
    /// </summary>
    public abstract MeshData GenerateMesh(int quality = 1);

    /// <summary>
    /// 获取实体的面集合
    /// </summary>
    public abstract List<SolidFace> GetFaces();

    /// <summary>
    /// 获取实体的边集合
    /// </summary>
    public abstract List<SolidEdge> GetEdges();

    /// <summary>
    /// 获取实体的顶点集合
    /// </summary>
    public abstract List<Point3d> GetVertices();

    /// <summary>
    /// 参数是否有效
    /// </summary>
    public abstract bool IsValid { get; }

    /// <summary>
    /// 重新生成几何(参数改变后调用)
    /// </summary>
    public abstract void Regenerate();
}

/// <summary>
/// 网格数据(用于渲染)
/// </summary>
public class MeshData
{
    public List<Point3d> Vertices { get; set; } = new();
    public List<Vector3d> Normals { get; set; } = new();
    public List<int> Indices { get; set; } = new();
    public List<Point2d> UVCoordinates { get; set; } = new();
}

/// <summary>
/// 实体面
/// </summary>
public class SolidFace
{
    public List<Point3d> Vertices { get; set; }
    public Vector3d Normal { get; set; }
    public FaceType Type { get; set; }
}

public enum FaceType
{
    Planar,       // 平面
    Cylindrical,  // 圆柱面
    Conical,      // 圆锥面
    Spherical,    // 球面
    Toroidal,     // 环面
    FreeForm      // 自由曲面
}

8.3 拉伸体(LcExtrude3d)

8.3.1 数据结构

拉伸是最基本的实体建模操作,将二维轮廓沿指定方向拉伸为三维实体:

public class LcExtrude3d : LcSolid3d
{
    /// <summary>
    /// 拉伸轮廓
    /// </summary>
    public LcProfile3d Profile { get; set; }

    /// <summary>
    /// 拉伸方向
    /// </summary>
    public Vector3d Direction { get; set; } = Vector3d.ZAxis;

    /// <summary>
    /// 拉伸距离
    /// </summary>
    public double Distance { get; set; }

    /// <summary>
    /// 拔模角度(拉伸时的锥度)
    /// </summary>
    public double DraftAngle { get; set; } = 0;

    /// <summary>
    /// 是否双向拉伸
    /// </summary>
    public bool IsBidirectional { get; set; } = false;

    /// <summary>
    /// 反向拉伸距离(双向拉伸时使用)
    /// </summary>
    public double ReverseDistance { get; set; }

    /// <summary>
    /// 拉伸终止类型
    /// </summary>
    public ExtrudeEndType EndType { get; set; } = ExtrudeEndType.Distance;

    public override string TypeName => "Extrude3d";

    public override double Volume
    {
        get
        {
            if (Math.Abs(DraftAngle) < 1e-10)
            {
                // 无拔模角,体积 = 面积 × 距离
                return Profile.Area * Distance;
            }
            else
            {
                // 有拔模角,需要数值积分
                return CalculateVolume();
            }
        }
    }

    public override MeshData GenerateMesh(int quality = 1)
    {
        var mesh = new MeshData();
        var profilePoints = Profile.OuterLoop.Tessellate();
        int segments = profilePoints.Count;

        // 生成底面和顶面
        GenerateCapMesh(mesh, profilePoints, 0, false);        // 底面
        GenerateCapMesh(mesh, profilePoints, Distance, true);  // 顶面

        // 生成侧面
        GenerateSideMesh(mesh, profilePoints);

        return mesh;
    }

    private void GenerateCapMesh(MeshData mesh, List<Point2d> profile,
                                  double height, bool isTop)
    {
        var baseIndex = mesh.Vertices.Count;
        var normal = isTop ? Direction : Direction * -1;

        foreach (var p in profile)
        {
            var worldPoint = Profile.WorkPlane.LocalToWorld(p);
            mesh.Vertices.Add(new Point3d(
                worldPoint.X + Direction.X * height,
                worldPoint.Y + Direction.Y * height,
                worldPoint.Z + Direction.Z * height
            ));
            mesh.Normals.Add(normal);
        }

        // 三角化(扇形三角化)
        for (int i = 1; i < profile.Count - 1; i++)
        {
            if (isTop)
            {
                mesh.Indices.Add(baseIndex);
                mesh.Indices.Add(baseIndex + i + 1);
                mesh.Indices.Add(baseIndex + i);
            }
            else
            {
                mesh.Indices.Add(baseIndex);
                mesh.Indices.Add(baseIndex + i);
                mesh.Indices.Add(baseIndex + i + 1);
            }
        }
    }

    private void GenerateSideMesh(MeshData mesh, List<Point2d> profile)
    {
        var baseIndex = mesh.Vertices.Count;

        for (int i = 0; i < profile.Count; i++)
        {
            int next = (i + 1) % profile.Count;

            // 底部顶点
            var bottomPoint = Profile.WorkPlane.LocalToWorld(profile[i]);
            // 顶部顶点
            var topPoint = new Point3d(
                bottomPoint.X + Direction.X * Distance,
                bottomPoint.Y + Direction.Y * Distance,
                bottomPoint.Z + Direction.Z * Distance
            );

            // 计算法向量
            var edge = Profile.WorkPlane.LocalToWorld(profile[next]);
            var edgeDir = new Vector3d(
                edge.X - bottomPoint.X,
                edge.Y - bottomPoint.Y,
                edge.Z - bottomPoint.Z).Normalize();
            var sideNormal = edgeDir.Cross(Direction).Normalize();

            mesh.Vertices.Add(bottomPoint);
            mesh.Normals.Add(sideNormal);
            mesh.Vertices.Add(topPoint);
            mesh.Normals.Add(sideNormal);
        }

        // 侧面三角形索引
        for (int i = 0; i < profile.Count; i++)
        {
            int next = (i + 1) % profile.Count;
            int bi = baseIndex + i * 2;
            int ni = baseIndex + next * 2;

            mesh.Indices.Add(bi);
            mesh.Indices.Add(bi + 1);
            mesh.Indices.Add(ni);

            mesh.Indices.Add(ni);
            mesh.Indices.Add(bi + 1);
            mesh.Indices.Add(ni + 1);
        }
    }
}

public enum ExtrudeEndType
{
    Distance,       // 指定距离
    ToFace,         // 延伸到面
    ThroughAll,     // 贯穿全部
    ToNextFace      // 延伸到下一个面
}

8.3.2 使用示例

// 创建一个矩形轮廓并拉伸
var profile = new LcProfile3d
{
    WorkPlane = new LcWorkPlane3d
    {
        Origin = Point3d.Origin,
        Normal = Vector3d.ZAxis
    },
    OuterLoop = CreateRectangle(0, 0, 100, 50)
};

var extrude = new LcExtrude3d
{
    Profile = profile,
    Direction = Vector3d.ZAxis,
    Distance = 30,
    DraftAngle = 0
};

// 带拔模角的拉伸(创建锥台)
var tapered = new LcExtrude3d
{
    Profile = profile,
    Direction = Vector3d.ZAxis,
    Distance = 50,
    DraftAngle = AngleUtils.DegToRad(5) // 5度拔模角
};

// 双向拉伸
var bidirectional = new LcExtrude3d
{
    Profile = profile,
    Direction = Vector3d.ZAxis,
    Distance = 20,
    IsBidirectional = true,
    ReverseDistance = 10
};

8.4 旋转体(LcRevolve3d)

8.4.1 数据结构

public class LcRevolve3d : LcSolid3d
{
    /// <summary>
    /// 旋转轮廓
    /// </summary>
    public LcProfile3d Profile { get; set; }

    /// <summary>
    /// 旋转轴上的一点
    /// </summary>
    public Point3d AxisPoint { get; set; }

    /// <summary>
    /// 旋转轴方向
    /// </summary>
    public Vector3d AxisDirection { get; set; } = Vector3d.YAxis;

    /// <summary>
    /// 旋转角度(弧度)
    /// </summary>
    public double Angle { get; set; } = 2 * Math.PI;

    /// <summary>
    /// 是否为完整旋转(360度)
    /// </summary>
    public bool IsFullRevolution => Math.Abs(Angle - 2 * Math.PI) < 1e-10;

    public override string TypeName => "Revolve3d";

    public override MeshData GenerateMesh(int quality = 1)
    {
        var mesh = new MeshData();
        var profilePoints = Profile.OuterLoop.Tessellate();
        int radialSegments = Math.Max(16, 32 * quality);

        // 对每个轮廓点,绕轴旋转生成顶点环
        for (int i = 0; i <= radialSegments; i++)
        {
            var t = (double)i / radialSegments;
            var currentAngle = Angle * t;
            var rotationMatrix = RotateAroundAxis(
                AxisPoint, AxisDirection, currentAngle);

            foreach (var p in profilePoints)
            {
                var worldPoint = Profile.WorkPlane.LocalToWorld(p);
                var rotated = rotationMatrix.Transform(worldPoint);
                mesh.Vertices.Add(rotated);

                // 计算法向量
                var normal = CalculateNormal(worldPoint, rotated, AxisPoint);
                mesh.Normals.Add(normal);
            }
        }

        // 生成三角形索引
        int profileCount = profilePoints.Count;
        for (int i = 0; i < radialSegments; i++)
        {
            for (int j = 0; j < profileCount - 1; j++)
            {
                int current = i * profileCount + j;
                int next = (i + 1) * profileCount + j;

                mesh.Indices.Add(current);
                mesh.Indices.Add(next);
                mesh.Indices.Add(current + 1);

                mesh.Indices.Add(current + 1);
                mesh.Indices.Add(next);
                mesh.Indices.Add(next + 1);
            }
        }

        return mesh;
    }
}

8.4.2 使用示例

// 创建一个花瓶形状(旋转体)
var vaseProfile = new LcPolyline();
vaseProfile.AddVertex(new Point2d(20, 0));    // 底部
vaseProfile.AddVertex(new Point2d(25, 10));
vaseProfile.AddVertex(new Point2d(15, 30));   // 收窄
vaseProfile.AddVertex(new Point2d(20, 50));
vaseProfile.AddVertex(new Point2d(30, 80));   // 口沿
vaseProfile.AddVertex(new Point2d(28, 85));
vaseProfile.AddVertex(new Point2d(0, 85));    // 内壁
vaseProfile.IsClosed = true;

var profile = new LcProfile3d
{
    WorkPlane = new LcWorkPlane3d { Origin = Point3d.Origin },
    OuterLoop = vaseProfile
};

var vase = new LcRevolve3d
{
    Profile = profile,
    AxisPoint = Point3d.Origin,
    AxisDirection = Vector3d.YAxis,
    Angle = 2 * Math.PI  // 完整旋转
};

8.5 放样体(LcLoft3d)

8.5.1 数据结构

放样操作在两个或多个截面轮廓之间创建平滑过渡的实体。LcLoft3d是建模系统中最复杂的操作之一(源代码文件达40KB):

public class LcLoft3d : LcSolid3d
{
    /// <summary>
    /// 截面轮廓集合(至少2个)
    /// </summary>
    public List<LcProfile3d> Profiles { get; set; } = new();

    /// <summary>
    /// 引导线(可选,用于控制过渡形状)
    /// </summary>
    public List<LcCurve3d> GuideCurves { get; set; } = new();

    /// <summary>
    /// 路径曲线(可选,截面沿路径放样)
    /// </summary>
    public LcCurve3d PathCurve { get; set; }

    /// <summary>
    /// 起始条件
    /// </summary>
    public LoftEndCondition StartCondition { get; set; } = LoftEndCondition.Free;

    /// <summary>
    /// 终止条件
    /// </summary>
    public LoftEndCondition EndCondition { get; set; } = LoftEndCondition.Free;

    /// <summary>
    /// 过渡类型
    /// </summary>
    public LoftTransitionType Transition { get; set; } = LoftTransitionType.Smooth;

    public override string TypeName => "Loft3d";

    public override MeshData GenerateMesh(int quality = 1)
    {
        var mesh = new MeshData();
        int steps = Math.Max(16, 32 * quality);

        // 对每个步骤,在截面之间插值
        for (int step = 0; step <= steps; step++)
        {
            var t = (double)step / steps;
            var interpolatedProfile = InterpolateProfiles(t);

            foreach (var point in interpolatedProfile)
            {
                mesh.Vertices.Add(point);
            }
        }

        // 生成三角形索引(连接相邻步骤的顶点)
        // ...

        return mesh;
    }

    /// <summary>
    /// 在截面之间进行插值
    /// </summary>
    private List<Point3d> InterpolateProfiles(double t)
    {
        if (Profiles.Count < 2)
            throw new InvalidOperationException("至少需要2个截面");

        // 确定当前t值对应的两个截面
        var sectionT = t * (Profiles.Count - 1);
        int index = Math.Min((int)sectionT, Profiles.Count - 2);
        var localT = sectionT - index;

        var profile1 = Profiles[index].GetWorldPoints();
        var profile2 = Profiles[index + 1].GetWorldPoints();

        // 确保两个轮廓的顶点数一致
        AlignVertexCounts(ref profile1, ref profile2);

        // 线性插值(可替换为样条插值以获得更平滑的效果)
        var result = new List<Point3d>();
        for (int i = 0; i < profile1.Count; i++)
        {
            result.Add(new Point3d(
                profile1[i].X * (1 - localT) + profile2[i].X * localT,
                profile1[i].Y * (1 - localT) + profile2[i].Y * localT,
                profile1[i].Z * (1 - localT) + profile2[i].Z * localT
            ));
        }

        return result;
    }
}

public enum LoftEndCondition
{
    Free,           // 自由
    Tangent,        // 与相邻面相切
    Perpendicular,  // 垂直于截面
    Smooth          // 平滑过渡
}

public enum LoftTransitionType
{
    Linear,         // 线性过渡
    Smooth,         // 平滑过渡
    RuleSurface     // 直纹面
}

8.6 融合体(LcBlend3d)

8.6.1 数据结构

融合操作用于在实体的边缘创建圆角或倒角:

public class LcBlend3d : LcSolid3d
{
    /// <summary>
    /// 要进行融合的目标实体
    /// </summary>
    public LcSolid3d TargetSolid { get; set; }

    /// <summary>
    /// 要融合的边
    /// </summary>
    public List<SolidEdge> SelectedEdges { get; set; } = new();

    /// <summary>
    /// 融合类型
    /// </summary>
    public BlendType Type { get; set; } = BlendType.Fillet;

    /// <summary>
    /// 圆角半径
    /// </summary>
    public double Radius { get; set; }

    /// <summary>
    /// 倒角距离1
    /// </summary>
    public double ChamferDistance1 { get; set; }

    /// <summary>
    /// 倒角距离2
    /// </summary>
    public double ChamferDistance2 { get; set; }

    /// <summary>
    /// 是否为变截面融合
    /// </summary>
    public bool IsVariable { get; set; } = false;

    public override string TypeName => "Blend3d";
}

public enum BlendType
{
    Fillet,     // 圆角
    Chamfer     // 倒角
}

8.7 推拉体(LcPushPull3d)

8.7.1 数据结构

推拉操作允许直接拉伸或收缩实体的面:

public class LcPushPull3d : LcSolid3d
{
    /// <summary>
    /// 目标实体
    /// </summary>
    public LcSolid3d TargetSolid { get; set; }

    /// <summary>
    /// 选中的面
    /// </summary>
    public SolidFace SelectedFace { get; set; }

    /// <summary>
    /// 推拉距离(正值向外,负值向内)
    /// </summary>
    public double Distance { get; set; }

    /// <summary>
    /// 拔模角度
    /// </summary>
    public double DraftAngle { get; set; }

    public override string TypeName => "PushPull3d";
}

8.8 网格体(LcMesh3d)

8.8.1 数据结构

LcMesh3d直接以三角网格的形式表示三维实体:

public class LcMesh3d : LcSolid3d
{
    /// <summary>
    /// 顶点列表
    /// </summary>
    public List<Point3d> Vertices { get; set; } = new();

    /// <summary>
    /// 法向量列表
    /// </summary>
    public List<Vector3d> Normals { get; set; } = new();

    /// <summary>
    /// 三角形面索引(每3个为一组)
    /// </summary>
    public List<int> FaceIndices { get; set; } = new();

    /// <summary>
    /// UV纹理坐标
    /// </summary>
    public List<Point2d> UVs { get; set; } = new();

    /// <summary>
    /// 三角形数量
    /// </summary>
    public int TriangleCount => FaceIndices.Count / 3;

    /// <summary>
    /// 顶点数量
    /// </summary>
    public int VertexCount => Vertices.Count;

    public override string TypeName => "Mesh3d";

    public override double Volume
    {
        get
        {
            // 使用Divergence Theorem计算网格体积
            double volume = 0;
            for (int i = 0; i < FaceIndices.Count; i += 3)
            {
                var v0 = Vertices[FaceIndices[i]];
                var v1 = Vertices[FaceIndices[i + 1]];
                var v2 = Vertices[FaceIndices[i + 2]];

                volume += SignedTriangleVolume(v0, v1, v2);
            }
            return Math.Abs(volume);
        }
    }

    private static double SignedTriangleVolume(Point3d a, Point3d b, Point3d c)
    {
        return (a.X * (b.Y * c.Z - b.Z * c.Y) +
                a.Y * (b.Z * c.X - b.X * c.Z) +
                a.Z * (b.X * c.Y - b.Y * c.X)) / 6.0;
    }

    public override MeshData GenerateMesh(int quality = 1)
    {
        return new MeshData
        {
            Vertices = new List<Point3d>(Vertices),
            Normals = new List<Vector3d>(Normals),
            Indices = new List<int>(FaceIndices),
            UVCoordinates = new List<Point2d>(UVs)
        };
    }

    /// <summary>
    /// 计算平滑法向量
    /// </summary>
    public void RecalculateNormals()
    {
        Normals = new List<Vector3d>(new Vector3d[Vertices.Count]);

        for (int i = 0; i < FaceIndices.Count; i += 3)
        {
            var i0 = FaceIndices[i];
            var i1 = FaceIndices[i + 1];
            var i2 = FaceIndices[i + 2];

            var v0 = Vertices[i0];
            var v1 = Vertices[i1];
            var v2 = Vertices[i2];

            var edge1 = new Vector3d(v1.X - v0.X, v1.Y - v0.Y, v1.Z - v0.Z);
            var edge2 = new Vector3d(v2.X - v0.X, v2.Y - v0.Y, v2.Z - v0.Z);
            var faceNormal = edge1.Cross(edge2);

            Normals[i0] = Normals[i0] + faceNormal;
            Normals[i1] = Normals[i1] + faceNormal;
            Normals[i2] = Normals[i2] + faceNormal;
        }

        for (int i = 0; i < Normals.Count; i++)
        {
            Normals[i] = Normals[i].Normalize();
        }
    }
}

8.9 布尔运算(LcCombination)

8.9.1 数据结构

布尔运算是实体建模中最强大的工具,可以通过并集、交集和差集来组合实体:

public class LcCombination : LcSolid3d
{
    /// <summary>
    /// 第一个操作实体
    /// </summary>
    public LcSolid3d SolidA { get; set; }

    /// <summary>
    /// 第二个操作实体
    /// </summary>
    public LcSolid3d SolidB { get; set; }

    /// <summary>
    /// 布尔运算类型
    /// </summary>
    public BooleanOperationType Operation { get; set; }

    public override string TypeName => "Combination";

    public override MeshData GenerateMesh(int quality = 1)
    {
        var meshA = SolidA.GenerateMesh(quality);
        var meshB = SolidB.GenerateMesh(quality);

        return Operation switch
        {
            BooleanOperationType.Union =>
                BooleanMesh.Union(meshA, meshB),
            BooleanOperationType.Intersect =>
                BooleanMesh.Intersect(meshA, meshB),
            BooleanOperationType.Subtract =>
                BooleanMesh.Subtract(meshA, meshB),
            _ => meshA
        };
    }
}

public enum BooleanOperationType
{
    Union,       // 并集:A ∪ B
    Intersect,   // 交集:A ∩ B
    Subtract     // 差集:A - B
}

8.9.2 使用示例

// 创建一个带孔的方块(差集运算)
var box = CreateBox(100, 100, 50);
var cylinder = CreateCylinder(
    new Point3d(50, 50, 0), 15, 50);

var boxWithHole = new LcCombination
{
    SolidA = box,
    SolidB = cylinder,
    Operation = BooleanOperationType.Subtract
};

// 创建两个圆柱的交集
var cyl1 = CreateCylinder(Point3d.Origin, 30, 100);
var cyl2 = CreateCylinder(
    new Point3d(50, 0, 0), 30, 100);

var intersection = new LcCombination
{
    SolidA = cyl1,
    SolidB = cyl2,
    Operation = BooleanOperationType.Intersect
};

8.10 组件操作(Component.Actions)

8.10.1 操作系统概述

LightCAD.Component.Actions模块提供了实际的建模操作实现,每个操作对应用户界面中的一个命令:

LightCAD.Component.Actions/
├── ComponentActionLoader.cs   # 操作加载器
├── CubeAction.cs              # 立方体操作
├── CuboidAction.cs            # 长方体操作(7KB)
├── ExtrudeAction.cs           # 拉伸操作(15KB)
└── LoftAction.cs              # 放样操作(40KB)

8.10.2 CuboidAction示例

public class CuboidAction : IComponentAction
{
    public string Name => "Cuboid";
    public string DisplayName => "长方体";
    public string Category => "基本实体";

    /// <summary>
    /// 执行创建长方体的操作
    /// </summary>
    public LcSolid3d Execute(ActionContext context)
    {
        // 获取用户输入的参数
        var origin = context.GetPoint("指定基点");
        var width = context.GetDistance("指定宽度");
        var height = context.GetDistance("指定高度");
        var depth = context.GetDistance("指定深度");

        // 创建矩形轮廓
        var profile = new LcProfile3d
        {
            WorkPlane = new LcWorkPlane3d { Origin = origin },
            OuterLoop = CreateRectangle(0, 0, width, height)
        };

        // 拉伸创建长方体
        return new LcExtrude3d
        {
            Profile = profile,
            Direction = Vector3d.ZAxis,
            Distance = depth
        };
    }
}

8.10.3 ExtrudeAction示例

public class ExtrudeAction : IComponentAction
{
    public string Name => "Extrude";
    public string DisplayName => "拉伸";

    public LcSolid3d Execute(ActionContext context)
    {
        // 选择要拉伸的轮廓
        var profile = context.SelectProfile("选择拉伸轮廓");

        // 获取拉伸参数
        var distance = context.GetDistance("指定拉伸距离");
        var direction = context.GetDirection("指定拉伸方向",
            profile.WorkPlane.Normal); // 默认法线方向

        var draftAngle = context.GetAngle("拔模角度(可选)", 0);

        return new LcExtrude3d
        {
            Profile = profile,
            Direction = direction,
            Distance = distance,
            DraftAngle = draftAngle
        };
    }
}

8.11 参数化建模

8.11.1 参数化特性

LightCAD的实体建模系统是参数化的,这意味着:

  1. 参数可修改:创建实体后可以随时修改参数
  2. 自动更新:修改参数后实体自动重新生成
  3. 历史记录:保留完整的建模操作历史
// 修改参数示例
var extrude = new LcExtrude3d { ... };

// 修改拉伸距离
extrude.Distance = 50;
extrude.Regenerate(); // 重新生成几何

// 修改轮廓
extrude.Profile.OuterLoop.Vertices[2] =
    new PolylineVertex(new Point2d(150, 50));
extrude.Regenerate();

8.11.2 特征树

LightCAD支持特征树(Feature Tree)来管理建模历史:

public class FeatureTree
{
    public List<Feature> Features { get; } = new();

    public void AddFeature(Feature feature)
    {
        Features.Add(feature);
        Rebuild();
    }

    public void RemoveFeature(int index)
    {
        Features.RemoveAt(index);
        Rebuild();
    }

    /// <summary>
    /// 重建所有特征
    /// </summary>
    public void Rebuild()
    {
        LcSolid3d currentSolid = null;

        foreach (var feature in Features)
        {
            currentSolid = feature.Apply(currentSolid);
        }
    }
}

public abstract class Feature
{
    public string Name { get; set; }
    public bool IsEnabled { get; set; } = true;
    public abstract LcSolid3d Apply(LcSolid3d input);
}

8.12 本章小结

本章全面介绍了LightCAD的实体建模系统。从基础的拉伸和旋转,到复杂的放样和布尔运算,LightCAD提供了完整的参数化实体建模能力。所有实体类型都继承自LcSolid3d基类,支持网格生成、体积计算和几何变换。组件操作模块(Component.Actions)将建模操作封装为可交互的命令,方便用户通过界面进行操作。参数化特性使得实体可以在创建后随时修改参数并自动更新。


上一章第七章:三维图元系统

下一章第九章:渲染系统与Three.js集成