第七章:三维图元系统
7.1 三维图元概述
7.1.1 三维图元分类
LightCAD的三维图元位于LightCAD.Core/Elements/Element3d/目录下,提供了完整的三维空间几何实体支持:
| 图元类型 | 类名 | 说明 |
|---|---|---|
| 三维直线 | LcLine3d | 三维空间中的线段 |
| 三维圆弧 | LcArc3d | 三维空间中的圆弧 |
| 三维圆 | LcCircle3d | 三维空间中的圆 |
| 三维曲线 | LcCurve3d | 通用三维曲线 |
| 三维轮廓 | LcProfile3d | 用于拉伸等操作的截面轮廓 |
| 工作平面 | LcWorkPlane3d | 自定义工作坐标系 |
| 三维组 | LcGroup3 | 三维实体的分组容器 |
| 三维阵列 | LcArray3 | 三维空间中的阵列复制 |
| 容器引用 | LcContianerRef3 | 组件容器引用 |
7.1.2 二维与三维图元的关系
二维图元(在XY平面上) 三维图元(在任意空间位置)
LcLine ──对应──> LcLine3d
LcArc ──对应──> LcArc3d
LcCircle ──对应──> LcCircle3d
LcSpline ──对应──> LcCurve3d
三维图元在二维图元的基础上增加了Z坐标和空间方位信息,能够表示任意空间位置和方向的几何实体。
7.2 三维直线(LcLine3d)
7.2.1 数据结构
public class LcLine3d : LcEntity, IUpdateObject
{
/// <summary>
/// 起点(三维坐标)
/// </summary>
public Point3d StartPoint { get; set; }
/// <summary>
/// 终点(三维坐标)
/// </summary>
public Point3d EndPoint { get; set; }
/// <summary>
/// 长度
/// </summary>
public double Length => StartPoint.DistanceTo(EndPoint);
/// <summary>
/// 方向向量
/// </summary>
public Vector3d Direction =>
new Vector3d(
EndPoint.X - StartPoint.X,
EndPoint.Y - StartPoint.Y,
EndPoint.Z - StartPoint.Z
).Normalize();
/// <summary>
/// 中点
/// </summary>
public Point3d MidPoint => new Point3d(
(StartPoint.X + EndPoint.X) / 2,
(StartPoint.Y + EndPoint.Y) / 2,
(StartPoint.Z + EndPoint.Z) / 2
);
public override string TypeName => "Line3d";
/// <summary>
/// 投影到XY平面获取二维线段
/// </summary>
public LcLine ToLine2d()
{
return new LcLine
{
StartPoint = StartPoint.ToPoint2d(),
EndPoint = EndPoint.ToPoint2d()
};
}
/// <summary>
/// 计算点到三维直线的距离
/// </summary>
public double DistanceTo3d(Point3d point)
{
var ab = new Vector3d(
EndPoint.X - StartPoint.X,
EndPoint.Y - StartPoint.Y,
EndPoint.Z - StartPoint.Z);
var ap = new Vector3d(
point.X - StartPoint.X,
point.Y - StartPoint.Y,
point.Z - StartPoint.Z);
var cross = ab.Cross(ap);
return cross.Length / ab.Length;
}
public override void TransformBy(Matrix4d matrix)
{
StartPoint = matrix.Transform(StartPoint);
EndPoint = matrix.Transform(EndPoint);
}
}
7.2.2 使用示例
// 创建一条在三维空间中的直线
var line3d = new LcLine3d
{
StartPoint = new Point3d(0, 0, 0),
EndPoint = new Point3d(100, 50, 75)
};
// 获取直线信息
Console.WriteLine($"长度: {line3d.Length}");
Console.WriteLine($"方向: {line3d.Direction}");
Console.WriteLine($"中点: {line3d.MidPoint}");
// 投影到二维
var line2d = line3d.ToLine2d();
7.3 三维圆弧(LcArc3d)
7.3.1 数据结构
三维圆弧需要额外的方向信息来确定其在空间中的位置:
public class LcArc3d : LcEntity, IUpdateObject
{
/// <summary>
/// 圆弧中心(三维坐标)
/// </summary>
public Point3d Center { get; set; }
/// <summary>
/// 半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 圆弧所在平面的法向量
/// </summary>
public Vector3d Normal { get; set; } = Vector3d.ZAxis;
/// <summary>
/// 起始角度
/// </summary>
public double StartAngle { get; set; }
/// <summary>
/// 终止角度
/// </summary>
public double EndAngle { get; set; }
/// <summary>
/// X轴方向(定义角度参考方向)
/// </summary>
public Vector3d XDirection { get; set; } = Vector3d.XAxis;
public override string TypeName => "Arc3d";
/// <summary>
/// 获取圆弧所在的平面
/// </summary>
public Plane GetPlane()
{
return new Plane(Center, Normal);
}
/// <summary>
/// 获取三维起点
/// </summary>
public Point3d StartPoint3d
{
get
{
var yDir = Normal.Cross(XDirection).Normalize();
return new Point3d(
Center.X + Radius * (Math.Cos(StartAngle) * XDirection.X +
Math.Sin(StartAngle) * yDir.X),
Center.Y + Radius * (Math.Cos(StartAngle) * XDirection.Y +
Math.Sin(StartAngle) * yDir.Y),
Center.Z + Radius * (Math.Cos(StartAngle) * XDirection.Z +
Math.Sin(StartAngle) * yDir.Z)
);
}
}
/// <summary>
/// 离散化为三维点序列
/// </summary>
public List<Point3d> Tessellate(int segments = 32)
{
var points = new List<Point3d>();
var yDir = Normal.Cross(XDirection).Normalize();
var sweep = EndAngle - StartAngle;
if (sweep < 0) sweep += 2 * Math.PI;
for (int i = 0; i <= segments; i++)
{
var t = (double)i / segments;
var angle = StartAngle + sweep * t;
var cos = Math.Cos(angle);
var sin = Math.Sin(angle);
points.Add(new Point3d(
Center.X + Radius * (cos * XDirection.X + sin * yDir.X),
Center.Y + Radius * (cos * XDirection.Y + sin * yDir.Y),
Center.Z + Radius * (cos * XDirection.Z + sin * yDir.Z)
));
}
return points;
}
}
7.4 三维圆(LcCircle3d)
7.4.1 数据结构
public class LcCircle3d : LcEntity, IUpdateObject
{
/// <summary>
/// 圆心(三维坐标)
/// </summary>
public Point3d Center { get; set; }
/// <summary>
/// 半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 法向量(圆所在平面的法线)
/// </summary>
public Vector3d Normal { get; set; } = Vector3d.ZAxis;
/// <summary>
/// 周长
/// </summary>
public double Circumference => 2 * Math.PI * Radius;
/// <summary>
/// 面积
/// </summary>
public double Area => Math.PI * Radius * Radius;
public override string TypeName => "Circle3d";
/// <summary>
/// 获取圆上指定角度的三维点
/// </summary>
public Point3d PointAt(double angle)
{
// 构建局部坐标系
var xDir = GetLocalXDirection();
var yDir = Normal.Cross(xDir).Normalize();
return new Point3d(
Center.X + Radius * (Math.Cos(angle) * xDir.X +
Math.Sin(angle) * yDir.X),
Center.Y + Radius * (Math.Cos(angle) * xDir.Y +
Math.Sin(angle) * yDir.Y),
Center.Z + Radius * (Math.Cos(angle) * xDir.Z +
Math.Sin(angle) * yDir.Z)
);
}
/// <summary>
/// 构建局部X方向
/// </summary>
private Vector3d GetLocalXDirection()
{
// 选择一个与法向量不共线的向量
var reference = Math.Abs(Normal.Dot(Vector3d.ZAxis)) < 0.9
? Vector3d.ZAxis : Vector3d.XAxis;
return reference.Cross(Normal).Normalize();
}
/// <summary>
/// 离散化为三维点序列
/// </summary>
public List<Point3d> Tessellate(int segments = 64)
{
var points = new List<Point3d>();
for (int i = 0; i <= segments; i++)
{
var angle = 2 * Math.PI * i / segments;
points.Add(PointAt(angle));
}
return points;
}
}
7.5 工作平面(LcWorkPlane3d)
7.5.1 概念说明
工作平面是LightCAD三维建模中的重要概念。它定义了一个局部坐标系,允许用户在任意方向的平面上进行二维绘图操作,然后将结果映射到三维空间中。
7.5.2 数据结构
public class LcWorkPlane3d : LcEntity, IUpdateObject
{
/// <summary>
/// 工作平面原点
/// </summary>
public Point3d Origin { get; set; }
/// <summary>
/// 平面法向量
/// </summary>
public Vector3d Normal { get; set; } = Vector3d.ZAxis;
/// <summary>
/// 局部X轴方向
/// </summary>
public Vector3d XAxis { get; set; } = Vector3d.XAxis;
/// <summary>
/// 局部Y轴方向(自动计算)
/// </summary>
public Vector3d YAxis => Normal.Cross(XAxis).Normalize();
/// <summary>
/// 显示大小(工作平面网格的范围)
/// </summary>
public double DisplaySize { get; set; } = 1000;
/// <summary>
/// 网格间距
/// </summary>
public double GridSpacing { get; set; } = 10;
/// <summary>
/// 是否显示网格
/// </summary>
public bool ShowGrid { get; set; } = true;
public override string TypeName => "WorkPlane3d";
/// <summary>
/// 将局部二维坐标转换为世界三维坐标
/// </summary>
public Point3d LocalToWorld(Point2d localPoint)
{
return new Point3d(
Origin.X + localPoint.X * XAxis.X + localPoint.Y * YAxis.X,
Origin.Y + localPoint.X * XAxis.Y + localPoint.Y * YAxis.Y,
Origin.Z + localPoint.X * XAxis.Z + localPoint.Y * YAxis.Z
);
}
/// <summary>
/// 将世界三维坐标转换为局部二维坐标
/// </summary>
public Point2d WorldToLocal(Point3d worldPoint)
{
var v = new Vector3d(
worldPoint.X - Origin.X,
worldPoint.Y - Origin.Y,
worldPoint.Z - Origin.Z
);
return new Point2d(
v.Dot(XAxis),
v.Dot(YAxis)
);
}
/// <summary>
/// 将三维点投影到工作平面上
/// </summary>
public Point3d ProjectPoint(Point3d point)
{
var local = WorldToLocal(point);
return LocalToWorld(local);
}
/// <summary>
/// 获取变换矩阵(局部到世界)
/// </summary>
public Matrix4d GetTransformMatrix()
{
return new Matrix4d(
XAxis.X, YAxis.X, Normal.X, Origin.X,
XAxis.Y, YAxis.Y, Normal.Y, Origin.Y,
XAxis.Z, YAxis.Z, Normal.Z, Origin.Z,
0, 0, 0, 1
);
}
}
7.5.3 使用示例
// 创建一个倾斜45度的工作平面
var workPlane = new LcWorkPlane3d
{
Origin = new Point3d(0, 0, 100),
Normal = new Vector3d(0, -Math.Sin(Math.PI / 4), Math.Cos(Math.PI / 4)),
XAxis = Vector3d.XAxis,
GridSpacing = 10,
ShowGrid = true
};
// 在工作平面上创建一个矩形
var p1 = workPlane.LocalToWorld(new Point2d(0, 0));
var p2 = workPlane.LocalToWorld(new Point2d(100, 0));
var p3 = workPlane.LocalToWorld(new Point2d(100, 50));
var p4 = workPlane.LocalToWorld(new Point2d(0, 50));
var rect = new LcPolyline();
rect.AddVertex(new Point2d(p1.X, p1.Y));
rect.AddVertex(new Point2d(p2.X, p2.Y));
rect.AddVertex(new Point2d(p3.X, p3.Y));
rect.AddVertex(new Point2d(p4.X, p4.Y));
rect.IsClosed = true;
7.6 三维组(LcGroup3)
7.6.1 数据结构
LcGroup3实现了组合模式(Composite Pattern),将多个三维实体组织为一个逻辑单元:
public class LcGroup3 : LcEntity, IUpdateObject
{
/// <summary>
/// 子实体集合
/// </summary>
public List<LcEntity> Children { get; set; } = new();
/// <summary>
/// 组的变换矩阵
/// </summary>
public Matrix4d Transform { get; set; } = Matrix4d.Identity;
/// <summary>
/// 组名称
/// </summary>
public string GroupName { get; set; }
public override string TypeName => "Group3";
/// <summary>
/// 添加子实体
/// </summary>
public void Add(LcEntity entity)
{
Children.Add(entity);
}
/// <summary>
/// 移除子实体
/// </summary>
public bool Remove(LcEntity entity)
{
return Children.Remove(entity);
}
/// <summary>
/// 将组展开为独立实体
/// </summary>
public List<LcEntity> Explode()
{
var result = new List<LcEntity>();
foreach (var child in Children)
{
var clone = child.Clone();
clone.TransformBy(Transform);
result.Add(clone);
}
return result;
}
public override void TransformBy(Matrix4d matrix)
{
Transform = matrix * Transform;
}
public override BoundingBox2d Bounds
{
get
{
if (Children.Count == 0)
return new BoundingBox2d();
var bounds = Children[0].Bounds;
for (int i = 1; i < Children.Count; i++)
{
bounds = bounds.Union(Children[i].Bounds);
}
return bounds;
}
}
}
7.7 三维阵列(LcArray3)
7.7.1 数据结构
LcArray3支持在三维空间中创建实体的参数化阵列复制:
public class LcArray3 : LcEntity, IUpdateObject
{
/// <summary>
/// 源实体
/// </summary>
public LcEntity SourceEntity { get; set; }
/// <summary>
/// 阵列类型
/// </summary>
public ArrayType Type { get; set; }
/// <summary>
/// X方向数量
/// </summary>
public int CountX { get; set; } = 1;
/// <summary>
/// Y方向数量
/// </summary>
public int CountY { get; set; } = 1;
/// <summary>
/// Z方向数量
/// </summary>
public int CountZ { get; set; } = 1;
/// <summary>
/// X方向间距
/// </summary>
public double SpacingX { get; set; }
/// <summary>
/// Y方向间距
/// </summary>
public double SpacingY { get; set; }
/// <summary>
/// Z方向间距
/// </summary>
public double SpacingZ { get; set; }
/// <summary>
/// 环形阵列中心
/// </summary>
public Point3d PolarCenter { get; set; }
/// <summary>
/// 环形阵列轴向量
/// </summary>
public Vector3d PolarAxis { get; set; } = Vector3d.ZAxis;
/// <summary>
/// 环形阵列总角度
/// </summary>
public double PolarAngle { get; set; } = 2 * Math.PI;
/// <summary>
/// 环形阵列数量
/// </summary>
public int PolarCount { get; set; }
public override string TypeName => "Array3";
/// <summary>
/// 生成所有阵列实例
/// </summary>
public List<LcEntity> GenerateInstances()
{
return Type switch
{
ArrayType.Rectangular => GenerateRectangular(),
ArrayType.Polar => GeneratePolar(),
_ => new List<LcEntity>()
};
}
private List<LcEntity> GenerateRectangular()
{
var instances = new List<LcEntity>();
for (int ix = 0; ix < CountX; ix++)
{
for (int iy = 0; iy < CountY; iy++)
{
for (int iz = 0; iz < CountZ; iz++)
{
if (ix == 0 && iy == 0 && iz == 0) continue;
var clone = SourceEntity.Clone();
var translation = Matrix4d.CreateTranslation(
ix * SpacingX,
iy * SpacingY,
iz * SpacingZ
);
clone.TransformBy(translation);
instances.Add(clone);
}
}
}
return instances;
}
private List<LcEntity> GeneratePolar()
{
var instances = new List<LcEntity>();
var angleStep = PolarAngle / PolarCount;
for (int i = 1; i < PolarCount; i++)
{
var clone = SourceEntity.Clone();
var angle = angleStep * i;
// 绕指定轴旋转
var toOrigin = Matrix4d.CreateTranslation(
-PolarCenter.X, -PolarCenter.Y, -PolarCenter.Z);
var rotation = CreateRotationAroundAxis(PolarAxis, angle);
var backToPos = Matrix4d.CreateTranslation(
PolarCenter.X, PolarCenter.Y, PolarCenter.Z);
clone.TransformBy(backToPos * rotation * toOrigin);
instances.Add(clone);
}
return instances;
}
}
public enum ArrayType
{
Rectangular, // 矩形阵列
Polar // 环形阵列
}
7.8 三维轮廓(LcProfile3d)
7.8.1 数据结构
LcProfile3d用于定义三维空间中的封闭轮廓,常用作拉伸、旋转等实体建模操作的截面:
public class LcProfile3d : LcEntity, IUpdateObject
{
/// <summary>
/// 轮廓所在的工作平面
/// </summary>
public LcWorkPlane3d WorkPlane { get; set; }
/// <summary>
/// 外轮廓曲线(必须闭合)
/// </summary>
public LcPolyline OuterLoop { get; set; }
/// <summary>
/// 内部孔洞轮廓(可选)
/// </summary>
public List<LcPolyline> InnerLoops { get; set; } = new();
/// <summary>
/// 轮廓面积(扣除孔洞)
/// </summary>
public double Area
{
get
{
var outerArea = OuterLoop.Area;
var innerArea = InnerLoops.Sum(loop => loop.Area);
return outerArea - innerArea;
}
}
public override string TypeName => "Profile3d";
/// <summary>
/// 获取轮廓的三维点序列
/// </summary>
public List<Point3d> GetWorldPoints()
{
var points2d = OuterLoop.Tessellate();
return points2d.Select(p => WorkPlane.LocalToWorld(p)).ToList();
}
/// <summary>
/// 验证轮廓是否有效
/// </summary>
public bool IsValid()
{
if (OuterLoop == null || !OuterLoop.IsClosed) return false;
if (OuterLoop.VertexCount < 3) return false;
// 检查内部轮廓是否都在外部轮廓内
foreach (var inner in InnerLoops)
{
if (!inner.IsClosed) return false;
foreach (var vertex in inner.Vertices)
{
var polygon = OuterLoop.Vertices
.Select(v => v.Position).ToList();
if (!GeometryUtils.PointInPolygon(vertex.Position, polygon))
return false;
}
}
return true;
}
}
7.9 容器引用(LcContianerRef3)
7.9.1 概念说明
LcContianerRef3是LightCAD中实现组件实例化和引用的机制。类似于AutoCAD中的块引用(Block Reference),它允许在文档中多次放置同一个组件定义的实例,而不必复制所有数据。
7.9.2 数据结构
public class LcContianerRef3 : LcEntity, IUpdateObject
{
/// <summary>
/// 引用的组件定义标识
/// </summary>
public LcGuid ComponentGuid { get; set; }
/// <summary>
/// 插入点(在世界坐标中的位置)
/// </summary>
public Point3d InsertionPoint { get; set; }
/// <summary>
/// 旋转角度
/// </summary>
public double Rotation { get; set; }
/// <summary>
/// 缩放因子
/// </summary>
public Vector3d Scale { get; set; } = new Vector3d(1, 1, 1);
/// <summary>
/// 完整的变换矩阵
/// </summary>
public Matrix4d InsertionMatrix
{
get
{
var scale = Matrix4d.CreateScale(Scale.X, Scale.Y, Scale.Z);
var rotation = Matrix4d.CreateRotationZ(Rotation);
var translation = Matrix4d.CreateTranslation(
InsertionPoint.X, InsertionPoint.Y, InsertionPoint.Z);
return translation * rotation * scale;
}
}
/// <summary>
/// 实例属性覆盖
/// </summary>
public Dictionary<string, object> PropertyOverrides { get; set; } = new();
public override string TypeName => "ContainerRef3";
/// <summary>
/// 获取引用的组件定义
/// </summary>
public LcGroup3 GetDefinition()
{
// 从文档的块定义集合中查找
return Document?.Blocks?.Find(ComponentGuid);
}
/// <summary>
/// 展开引用为独立实体
/// </summary>
public List<LcEntity> Explode()
{
var definition = GetDefinition();
if (definition == null) return new List<LcEntity>();
var result = new List<LcEntity>();
foreach (var child in definition.Children)
{
var clone = child.Clone();
clone.TransformBy(InsertionMatrix);
result.Add(clone);
}
return result;
}
}
7.10 三维图元的坐标变换
7.10.1 世界坐标与局部坐标
在三维建模中,坐标变换是核心操作。LightCAD中的三维实体需要在不同坐标系之间转换:
// 世界坐标系(WCS)
// 全局固定的笛卡尔坐标系
// 用户坐标系(UCS)
// 用户自定义的坐标系,通过工作平面定义
// 对象坐标系(OCS)
// 实体自身的局部坐标系
// WCS → OCS 变换
public Point3d WorldToObject(Point3d worldPoint, Matrix4d objectTransform)
{
var inverseTransform = objectTransform.Inverse();
return inverseTransform.Transform(worldPoint);
}
// OCS → WCS 变换
public Point3d ObjectToWorld(Point3d objectPoint, Matrix4d objectTransform)
{
return objectTransform.Transform(objectPoint);
}
7.10.2 常用三维变换
// 绕任意轴旋转
public static Matrix4d RotateAroundAxis(Point3d point, Vector3d axis, double angle)
{
var toOrigin = Matrix4d.CreateTranslation(-point.X, -point.Y, -point.Z);
var backToPos = Matrix4d.CreateTranslation(point.X, point.Y, point.Z);
var n = axis.Normalize();
var cos = Math.Cos(angle);
var sin = Math.Sin(angle);
var t = 1 - cos;
var rotation = new Matrix4d(
t * n.X * n.X + cos, t * n.X * n.Y - sin * n.Z, t * n.X * n.Z + sin * n.Y, 0,
t * n.X * n.Y + sin * n.Z, t * n.Y * n.Y + cos, t * n.Y * n.Z - sin * n.X, 0,
t * n.X * n.Z - sin * n.Y, t * n.Y * n.Z + sin * n.X, t * n.Z * n.Z + cos, 0,
0, 0, 0, 1
);
return backToPos * rotation * toOrigin;
}
// 三维镜像(关于平面镜像)
public static Matrix4d MirrorAboutPlane(Plane plane)
{
var n = plane.Normal.Normalize();
var d = -n.Dot(new Vector3d(
plane.Origin.X, plane.Origin.Y, plane.Origin.Z));
return new Matrix4d(
1 - 2 * n.X * n.X, -2 * n.X * n.Y, -2 * n.X * n.Z, -2 * n.X * d,
-2 * n.X * n.Y, 1 - 2 * n.Y * n.Y, -2 * n.Y * n.Z, -2 * n.Y * d,
-2 * n.X * n.Z, -2 * n.Y * n.Z, 1 - 2 * n.Z * n.Z, -2 * n.Z * d,
0, 0, 0, 1
);
}
7.11 本章小结
本章详细介绍了LightCAD的三维图元系统。三维图元在二维图元的基础上增加了Z坐标和空间方位信息,能够表示任意空间位置和方向的几何实体。工作平面(LcWorkPlane3d)提供了在任意方向的平面上进行绘图的能力,三维组(LcGroup3)和阵列(LcArray3)实现了实体的组织和批量复制,容器引用(LcContianerRef3)提供了高效的组件实例化机制。这些三维图元为后续的实体建模和视图构建奠定了基础。
上一章:第六章:二维图元系统
下一章:第八章:实体建模系统