第六章:二维图元系统
6.1 二维图元概述
6.1.1 图元分类
LightCAD的二维图元系统位于LightCAD.Core/Elements/Basic/目录下,包含了CAD系统中所有基本的二维几何实体。这些图元是构成复杂设计图纸的基本元素:
| 图元类型 | 类名 | 说明 |
|---|---|---|
| 直线 | LcLine | 由起点和终点定义的线段 |
| 圆弧 | LcArc | 由圆心、半径、起始角和终止角定义 |
| 圆 | LcCircle | 由圆心和半径定义的完整圆 |
| 椭圆 | LcEllipse | 由中心、长轴、短轴和旋转角定义 |
| 多段线 | LcPolyline | 由顶点序列定义的折线或曲线 |
| 多边形 | LcPolygon | 封闭的多段线 |
| 样条曲线 | LcSpline | 通过控制点定义的光滑曲线 |
6.1.2 公共基类
所有二维图元都继承自LcEntity基类,共享以下属性:
// 所有2D图元的公共属性(来自LcEntity)
public string LayerName { get; set; } // 图层
public LcColor Color { get; set; } // 颜色
public string LineType { get; set; } // 线型
public double LineWeight { get; set; } // 线宽
public bool IsVisible { get; set; } // 可见性
6.2 直线(LcLine)
6.2.1 数据结构
public class LcLine : LcEntity, IUpdateObject
{
/// <summary>
/// 直线起点
/// </summary>
public Point2d StartPoint { get; set; }
/// <summary>
/// 直线终点
/// </summary>
public Point2d EndPoint { get; set; }
/// <summary>
/// 直线长度
/// </summary>
public double Length => StartPoint.DistanceTo(EndPoint);
/// <summary>
/// 直线方向向量
/// </summary>
public Vector2d Direction => (EndPoint - StartPoint).Normalize();
/// <summary>
/// 直线中点
/// </summary>
public Point2d MidPoint => StartPoint.MidPoint(EndPoint);
/// <summary>
/// 直线与X轴的夹角
/// </summary>
public double Angle
{
get
{
var dir = EndPoint - StartPoint;
return Math.Atan2(dir.Y, dir.X);
}
}
public override string TypeName => "Line";
public override BoundingBox2d Bounds
{
get
{
return new BoundingBox2d
{
Min = new Point2d(
Math.Min(StartPoint.X, EndPoint.X),
Math.Min(StartPoint.Y, EndPoint.Y)),
Max = new Point2d(
Math.Max(StartPoint.X, EndPoint.X),
Math.Max(StartPoint.Y, EndPoint.Y))
};
}
}
}
6.2.2 构造方法
// 方法1:通过两个点创建
var line1 = new LcLine
{
StartPoint = new Point2d(0, 0),
EndPoint = new Point2d(100, 50)
};
// 方法2:通过起点、角度和长度创建
public static LcLine FromAngleAndLength(
Point2d start, double angle, double length)
{
var end = new Point2d(
start.X + length * Math.Cos(angle),
start.Y + length * Math.Sin(angle)
);
return new LcLine { StartPoint = start, EndPoint = end };
}
// 方法3:通过起点和偏移量创建
public static LcLine FromOffset(
Point2d start, double dx, double dy)
{
return new LcLine
{
StartPoint = start,
EndPoint = new Point2d(start.X + dx, start.Y + dy)
};
}
6.2.3 几何操作
public class LcLine : LcEntity
{
/// <summary>
/// 计算点到直线的最近点
/// </summary>
public Point2d ClosestPoint(Point2d point)
{
return GeometryUtils.ClosestPointOnSegment(
point, StartPoint, EndPoint);
}
/// <summary>
/// 计算点到直线的距离
/// </summary>
public override double DistanceTo(Point2d point)
{
var closest = ClosestPoint(point);
return point.DistanceTo(closest);
}
/// <summary>
/// 在指定参数位置分割直线
/// </summary>
public (LcLine first, LcLine second) SplitAt(double t)
{
var splitPoint = new Point2d(
StartPoint.X + t * (EndPoint.X - StartPoint.X),
StartPoint.Y + t * (EndPoint.Y - StartPoint.Y)
);
return (
new LcLine { StartPoint = StartPoint, EndPoint = splitPoint },
new LcLine { StartPoint = splitPoint, EndPoint = EndPoint }
);
}
/// <summary>
/// 延伸直线到指定长度
/// </summary>
public void ExtendTo(double newLength)
{
var dir = Direction;
EndPoint = StartPoint + dir * newLength;
}
/// <summary>
/// 修剪直线到指定的边界
/// </summary>
public void TrimTo(Point2d boundaryStart, Vector2d boundaryDir)
{
var intersection = LineLineIntersect.Compute(
StartPoint, Direction, boundaryStart, boundaryDir);
if (intersection.HasValue)
{
EndPoint = intersection.Value;
}
}
/// <summary>
/// 几何变换
/// </summary>
public override void TransformBy(Matrix4d matrix)
{
var start3d = new Point3d(StartPoint.X, StartPoint.Y, 0);
var end3d = new Point3d(EndPoint.X, EndPoint.Y, 0);
var newStart = matrix.Transform(start3d);
var newEnd = matrix.Transform(end3d);
StartPoint = new Point2d(newStart.X, newStart.Y);
EndPoint = new Point2d(newEnd.X, newEnd.Y);
}
/// <summary>
/// 克隆
/// </summary>
public override LcEntity Clone()
{
return new LcLine
{
StartPoint = StartPoint,
EndPoint = EndPoint,
Color = Color,
LayerName = LayerName,
LineType = LineType,
LineWeight = LineWeight
};
}
}
6.3 圆弧(LcArc)
6.3.1 数据结构
public class LcArc : LcEntity, IUpdateObject
{
/// <summary>
/// 圆心
/// </summary>
public Point2d Center { get; set; }
/// <summary>
/// 半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 起始角度(弧度)
/// </summary>
public double StartAngle { get; set; }
/// <summary>
/// 终止角度(弧度)
/// </summary>
public double EndAngle { get; set; }
/// <summary>
/// 是否为逆时针方向
/// </summary>
public bool IsCounterClockwise { get; set; } = true;
/// <summary>
/// 圆弧长度
/// </summary>
public double Length
{
get
{
var sweep = SweepAngle;
return Radius * Math.Abs(sweep);
}
}
/// <summary>
/// 扫掠角度
/// </summary>
public double SweepAngle
{
get
{
var sweep = EndAngle - StartAngle;
if (IsCounterClockwise && sweep < 0)
sweep += 2 * Math.PI;
if (!IsCounterClockwise && sweep > 0)
sweep -= 2 * Math.PI;
return sweep;
}
}
/// <summary>
/// 起点
/// </summary>
public Point2d StartPoint => ArcMath.PointOnArc(Center, Radius, StartAngle);
/// <summary>
/// 终点
/// </summary>
public Point2d EndPoint => ArcMath.PointOnArc(Center, Radius, EndAngle);
/// <summary>
/// 中点
/// </summary>
public Point2d MidPoint
{
get
{
var midAngle = StartAngle + SweepAngle / 2;
return ArcMath.PointOnArc(Center, Radius, midAngle);
}
}
public override string TypeName => "Arc";
}
6.3.2 圆弧的创建方式
// 方法1:圆心、半径、起止角度
var arc1 = new LcArc
{
Center = new Point2d(50, 50),
Radius = 30,
StartAngle = 0,
EndAngle = Math.PI / 2 // 90度圆弧
};
// 方法2:三点定圆弧
public static LcArc FromThreePoints(Point2d start, Point2d through, Point2d end)
{
var arcData = ArcMath.FromThreePoints(start, through, end);
return new LcArc
{
Center = arcData.Center,
Radius = arcData.Radius,
StartAngle = arcData.StartAngle,
EndAngle = arcData.EndAngle
};
}
// 方法3:起点、终点、凸度
public static LcArc FromBulge(Point2d start, Point2d end, double bulge)
{
// bulge = tan(sweepAngle/4)
var chord = start.DistanceTo(end);
var sagitta = Math.Abs(bulge) * chord / 2;
var radius = (chord * chord / 4 + sagitta * sagitta) / (2 * sagitta);
var midPoint = start.MidPoint(end);
var chordDir = (end - start).Normalize();
var normalDir = chordDir.Perpendicular();
var offset = radius - sagitta;
var center = midPoint + normalDir * (bulge > 0 ? -offset : offset);
return new LcArc
{
Center = center,
Radius = radius,
StartAngle = Math.Atan2(start.Y - center.Y, start.X - center.X),
EndAngle = Math.Atan2(end.Y - center.Y, end.X - center.X),
IsCounterClockwise = bulge > 0
};
}
6.3.3 离散化
/// <summary>
/// 将圆弧离散为折线段(用于渲染)
/// </summary>
public List<Point2d> Tessellate(int segments = 0)
{
// 如果未指定段数,根据半径和扫掠角自动计算
if (segments <= 0)
{
segments = Math.Max(8, (int)(Math.Abs(SweepAngle) * Radius / 5));
segments = Math.Min(segments, 360);
}
return ArcMath.Tessellate(Center, Radius, StartAngle, EndAngle, segments);
}
6.4 圆(LcCircle)
6.4.1 数据结构
public class LcCircle : LcEntity, IUpdateObject
{
/// <summary>
/// 圆心
/// </summary>
public Point2d Center { get; set; }
/// <summary>
/// 半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 周长
/// </summary>
public double Circumference => 2 * Math.PI * Radius;
/// <summary>
/// 面积
/// </summary>
public double Area => Math.PI * Radius * Radius;
/// <summary>
/// 直径
/// </summary>
public double Diameter => 2 * Radius;
public override string TypeName => "Circle";
public override BoundingBox2d Bounds
{
get
{
return new BoundingBox2d
{
Min = new Point2d(Center.X - Radius, Center.Y - Radius),
Max = new Point2d(Center.X + Radius, Center.Y + Radius)
};
}
}
/// <summary>
/// 判断点是否在圆内
/// </summary>
public bool ContainsPoint(Point2d point)
{
return Center.DistanceTo(point) <= Radius + Tolerance.Distance;
}
/// <summary>
/// 获取圆上指定角度的点
/// </summary>
public Point2d PointAt(double angle)
{
return new Point2d(
Center.X + Radius * Math.Cos(angle),
Center.Y + Radius * Math.Sin(angle)
);
}
/// <summary>
/// 获取从外部点到圆的切线
/// </summary>
public (LcLine tangent1, LcLine tangent2)? GetTangentLines(Point2d point)
{
var dist = Center.DistanceTo(point);
if (dist <= Radius) return null; // 点在圆内
var angle = Math.Acos(Radius / dist);
var dirAngle = Math.Atan2(
Center.Y - point.Y, Center.X - point.X);
var tangentPoint1 = PointAt(dirAngle + Math.PI + angle);
var tangentPoint2 = PointAt(dirAngle + Math.PI - angle);
return (
new LcLine { StartPoint = point, EndPoint = tangentPoint1 },
new LcLine { StartPoint = point, EndPoint = tangentPoint2 }
);
}
}
6.4.2 圆的创建方式
// 方法1:圆心和半径
var circle1 = new LcCircle
{
Center = new Point2d(100, 100),
Radius = 50
};
// 方法2:两点确定(对径点)
public static LcCircle FromDiameter(Point2d p1, Point2d p2)
{
var center = p1.MidPoint(p2);
var radius = p1.DistanceTo(p2) / 2;
return new LcCircle { Center = center, Radius = radius };
}
// 方法3:三点确定
public static LcCircle FromThreePoints(Point2d p1, Point2d p2, Point2d p3)
{
var arcData = ArcMath.FromThreePoints(p1, p2, p3);
return new LcCircle
{
Center = arcData.Center,
Radius = arcData.Radius
};
}
// 方法4:相切于两条线(TTR - 相切、相切、半径)
public static LcCircle TangentTangentRadius(
LcLine line1, LcLine line2, double radius)
{
// 计算两条线的偏移线(距离为radius)
// 然后求偏移线的交点作为圆心
// ...
}
6.5 椭圆(LcEllipse)
6.5.1 数据结构
public class LcEllipse : LcEntity, IUpdateObject
{
/// <summary>
/// 椭圆中心
/// </summary>
public Point2d Center { get; set; }
/// <summary>
/// 长半轴长度
/// </summary>
public double MajorRadius { get; set; }
/// <summary>
/// 短半轴长度
/// </summary>
public double MinorRadius { get; set; }
/// <summary>
/// 旋转角度(长轴与X轴的夹角,弧度)
/// </summary>
public double Rotation { get; set; }
/// <summary>
/// 起始参数(用于椭圆弧)
/// </summary>
public double StartParameter { get; set; } = 0;
/// <summary>
/// 终止参数(用于椭圆弧,2π表示完整椭圆)
/// </summary>
public double EndParameter { get; set; } = 2 * Math.PI;
/// <summary>
/// 是否为完整椭圆
/// </summary>
public bool IsFullEllipse =>
Math.Abs(EndParameter - StartParameter - 2 * Math.PI) < 1e-10;
/// <summary>
/// 离心率
/// </summary>
public double Eccentricity
{
get
{
var a = Math.Max(MajorRadius, MinorRadius);
var b = Math.Min(MajorRadius, MinorRadius);
return Math.Sqrt(1 - (b * b) / (a * a));
}
}
public override string TypeName => "Ellipse";
/// <summary>
/// 获取椭圆上指定参数处的点
/// </summary>
public Point2d PointAt(double t)
{
var cos = Math.Cos(Rotation);
var sin = Math.Sin(Rotation);
var x = MajorRadius * Math.Cos(t);
var y = MinorRadius * Math.Sin(t);
return new Point2d(
Center.X + x * cos - y * sin,
Center.Y + x * sin + y * cos
);
}
/// <summary>
/// 周长(近似计算,使用Ramanujan公式)
/// </summary>
public double Circumference
{
get
{
var a = MajorRadius;
var b = MinorRadius;
var h = Math.Pow(a - b, 2) / Math.Pow(a + b, 2);
return Math.PI * (a + b) * (1 + 3 * h / (10 + Math.Sqrt(4 - 3 * h)));
}
}
}
6.6 多段线(LcPolyline)
6.6.1 数据结构
多段线是CAD中最常用的图元之一,由一系列顶点组成,顶点之间可以是直线段或圆弧段:
public class LcPolyline : LcEntity, IUpdateObject
{
/// <summary>
/// 顶点集合
/// </summary>
public List<PolylineVertex> Vertices { get; set; } = new();
/// <summary>
/// 是否闭合
/// </summary>
public bool IsClosed { get; set; }
/// <summary>
/// 全局宽度
/// </summary>
public double GlobalWidth { get; set; }
/// <summary>
/// 顶点数量
/// </summary>
public int VertexCount => Vertices.Count;
/// <summary>
/// 线段数量
/// </summary>
public int SegmentCount => IsClosed ? Vertices.Count : Vertices.Count - 1;
public override string TypeName => "Polyline";
/// <summary>
/// 总长度
/// </summary>
public double Length
{
get
{
double length = 0;
for (int i = 0; i < SegmentCount; i++)
{
length += GetSegmentLength(i);
}
return length;
}
}
/// <summary>
/// 面积(仅对闭合多段线有效)
/// </summary>
public double Area
{
get
{
if (!IsClosed) return 0;
var points = Vertices.Select(v => v.Position).ToList();
return GeometryUtils.PolygonArea(points);
}
}
}
/// <summary>
/// 多段线顶点
/// </summary>
public struct PolylineVertex
{
/// <summary>
/// 顶点坐标
/// </summary>
public Point2d Position { get; set; }
/// <summary>
/// 凸度(0为直线段,非0为圆弧段)
/// bulge = tan(包含角/4)
/// 正值为逆时针,负值为顺时针
/// </summary>
public double Bulge { get; set; }
/// <summary>
/// 起始宽度
/// </summary>
public double StartWidth { get; set; }
/// <summary>
/// 终止宽度
/// </summary>
public double EndWidth { get; set; }
public PolylineVertex(Point2d position, double bulge = 0)
{
Position = position;
Bulge = bulge;
StartWidth = 0;
EndWidth = 0;
}
/// <summary>
/// 是否为圆弧段
/// </summary>
public bool IsArc => Math.Abs(Bulge) > 1e-10;
}
6.6.2 多段线操作
public class LcPolyline : LcEntity
{
/// <summary>
/// 添加顶点
/// </summary>
public void AddVertex(Point2d point, double bulge = 0)
{
Vertices.Add(new PolylineVertex(point, bulge));
}
/// <summary>
/// 在指定位置插入顶点
/// </summary>
public void InsertVertex(int index, Point2d point, double bulge = 0)
{
Vertices.Insert(index, new PolylineVertex(point, bulge));
}
/// <summary>
/// 删除顶点
/// </summary>
public void RemoveVertex(int index)
{
Vertices.RemoveAt(index);
}
/// <summary>
/// 获取指定段的起止顶点
/// </summary>
public (PolylineVertex start, PolylineVertex end) GetSegment(int index)
{
var start = Vertices[index];
var end = Vertices[(index + 1) % Vertices.Count];
return (start, end);
}
/// <summary>
/// 获取指定段的长度
/// </summary>
public double GetSegmentLength(int index)
{
var (start, end) = GetSegment(index);
if (start.IsArc)
{
// 圆弧段长度
var arc = LcArc.FromBulge(start.Position, end.Position, start.Bulge);
return arc.Length;
}
else
{
// 直线段长度
return start.Position.DistanceTo(end.Position);
}
}
/// <summary>
/// 反转多段线方向
/// </summary>
public void Reverse()
{
Vertices.Reverse();
// 反转凸度符号
for (int i = 0; i < Vertices.Count; i++)
{
var v = Vertices[i];
Vertices[i] = new PolylineVertex(v.Position, -v.Bulge);
}
}
/// <summary>
/// 偏移多段线
/// </summary>
public LcPolyline Offset(double distance)
{
var result = new LcPolyline { IsClosed = IsClosed };
for (int i = 0; i < SegmentCount; i++)
{
var (start, end) = GetSegment(i);
var dir = (end.Position - start.Position).Normalize();
var normal = dir.Perpendicular();
result.AddVertex(
start.Position + normal * distance,
start.Bulge);
}
if (!IsClosed)
{
var lastVertex = Vertices.Last();
var prevVertex = Vertices[Vertices.Count - 2];
var dir = (lastVertex.Position - prevVertex.Position).Normalize();
var normal = dir.Perpendicular();
result.AddVertex(lastVertex.Position + normal * distance);
}
return result;
}
/// <summary>
/// 离散化为点列表
/// </summary>
public List<Point2d> Tessellate(int arcSegments = 16)
{
var points = new List<Point2d>();
for (int i = 0; i < SegmentCount; i++)
{
var (start, end) = GetSegment(i);
points.Add(start.Position);
if (start.IsArc)
{
// 将圆弧段离散为折线点
var arc = LcArc.FromBulge(
start.Position, end.Position, start.Bulge);
var arcPoints = arc.Tessellate(arcSegments);
// 跳过第一个和最后一个点(避免重复)
for (int j = 1; j < arcPoints.Count - 1; j++)
{
points.Add(arcPoints[j]);
}
}
}
// 添加最后一个顶点(非闭合情况)
if (!IsClosed && Vertices.Count > 0)
{
points.Add(Vertices.Last().Position);
}
return points;
}
}
6.7 多边形(LcPolygon)
6.7.1 数据结构
LcPolygon本质上是一个闭合的多段线,但提供了额外的面域相关操作:
public class LcPolygon : LcEntity, IUpdateObject
{
/// <summary>
/// 顶点集合
/// </summary>
public List<Point2d> Vertices { get; set; } = new();
/// <summary>
/// 面积
/// </summary>
public double Area => GeometryUtils.PolygonArea(Vertices);
/// <summary>
/// 周长
/// </summary>
public double Perimeter
{
get
{
double perimeter = 0;
for (int i = 0; i < Vertices.Count; i++)
{
int j = (i + 1) % Vertices.Count;
perimeter += Vertices[i].DistanceTo(Vertices[j]);
}
return perimeter;
}
}
/// <summary>
/// 重心
/// </summary>
public Point2d Centroid
{
get
{
double cx = 0, cy = 0;
double signedArea = 0;
for (int i = 0; i < Vertices.Count; i++)
{
int j = (i + 1) % Vertices.Count;
var cross = Vertices[i].X * Vertices[j].Y -
Vertices[j].X * Vertices[i].Y;
signedArea += cross;
cx += (Vertices[i].X + Vertices[j].X) * cross;
cy += (Vertices[i].Y + Vertices[j].Y) * cross;
}
signedArea /= 2;
cx /= (6 * signedArea);
cy /= (6 * signedArea);
return new Point2d(cx, cy);
}
}
/// <summary>
/// 判断点是否在多边形内
/// </summary>
public bool ContainsPoint(Point2d point)
{
return GeometryUtils.PointInPolygon(point, Vertices);
}
/// <summary>
/// 是否为凸多边形
/// </summary>
public bool IsConvex
{
get
{
int n = Vertices.Count;
if (n < 3) return false;
bool? sign = null;
for (int i = 0; i < n; i++)
{
var d1 = Vertices[(i + 1) % n] - Vertices[i];
var d2 = Vertices[(i + 2) % n] - Vertices[(i + 1) % n];
var cross = d1.Cross(d2);
if (Math.Abs(cross) > 1e-10)
{
bool positive = cross > 0;
if (sign.HasValue && sign.Value != positive)
return false;
sign = positive;
}
}
return true;
}
}
public override string TypeName => "Polygon";
}
6.7.2 正多边形创建
/// <summary>
/// 创建正多边形
/// </summary>
public static LcPolygon CreateRegular(
Point2d center, double radius, int sides, double startAngle = 0)
{
var polygon = new LcPolygon();
for (int i = 0; i < sides; i++)
{
var angle = startAngle + 2 * Math.PI * i / sides;
polygon.Vertices.Add(new Point2d(
center.X + radius * Math.Cos(angle),
center.Y + radius * Math.Sin(angle)
));
}
return polygon;
}
// 使用示例
var hexagon = LcPolygon.CreateRegular(
new Point2d(50, 50), 30, 6); // 六边形
var pentagon = LcPolygon.CreateRegular(
new Point2d(150, 50), 30, 5); // 五边形
6.8 样条曲线(LcSpline)
6.8.1 数据结构
public class LcSpline : LcEntity, IUpdateObject
{
/// <summary>
/// 拟合点(曲线经过的点)
/// </summary>
public List<Point2d> FitPoints { get; set; } = new();
/// <summary>
/// 控制点(用于NURBS表示)
/// </summary>
public List<Point2d> ControlPoints { get; set; } = new();
/// <summary>
/// 节点向量
/// </summary>
public List<double> KnotVector { get; set; } = new();
/// <summary>
/// 曲线阶数
/// </summary>
public int Degree { get; set; } = 3;
/// <summary>
/// 权重
/// </summary>
public List<double> Weights { get; set; } = new();
/// <summary>
/// 是否闭合
/// </summary>
public bool IsClosed { get; set; }
/// <summary>
/// 起点切线方向
/// </summary>
public Vector2d? StartTangent { get; set; }
/// <summary>
/// 终点切线方向
/// </summary>
public Vector2d? EndTangent { get; set; }
public override string TypeName => "Spline";
/// <summary>
/// 计算曲线上指定参数处的点
/// </summary>
public Point2d PointAt(double t)
{
if (ControlPoints.Count > 0 && KnotVector.Count > 0)
{
// NURBS求值
var nurbs = new NurbsCurve(
ControlPoints.Select(
p => new Point3d(p.X, p.Y, 0)).ToArray(),
Weights.Count > 0 ?
Weights.ToArray() :
Enumerable.Repeat(1.0, ControlPoints.Count).ToArray(),
KnotVector.ToArray(),
Degree
);
var result = nurbs.Evaluate(t);
return new Point2d(result.X, result.Y);
}
else if (FitPoints.Count >= 2)
{
// 通过拟合点插值
return InterpolateFitPoints(t);
}
return Point2d.Origin;
}
/// <summary>
/// 离散化为点列表
/// </summary>
public List<Point2d> Tessellate(int segments = 64)
{
var points = new List<Point2d>(segments + 1);
for (int i = 0; i <= segments; i++)
{
var t = (double)i / segments;
points.Add(PointAt(t));
}
return points;
}
}
6.9 图元的通用操作
6.9.1 几何变换
所有二维图元都支持以下几何变换:
// 平移
var translation = Matrix4d.CreateTranslation(dx, dy, 0);
entity.TransformBy(translation);
// 旋转(绕指定点)
var toOrigin = Matrix4d.CreateTranslation(-centerX, -centerY, 0);
var rotation = Matrix4d.CreateRotationZ(angle);
var backToPos = Matrix4d.CreateTranslation(centerX, centerY, 0);
entity.TransformBy(backToPos * rotation * toOrigin);
// 缩放(绕指定点)
var scale = Matrix4d.CreateScale(sx, sy, 1);
entity.TransformBy(backToPos * scale * toOrigin);
// 镜像(沿指定轴线)
public static Matrix4d CreateMirror(Point2d linePoint, Vector2d lineDir)
{
var dir = lineDir.Normalize();
// 反射矩阵公式
return new Matrix4d(
2 * dir.X * dir.X - 1, 2 * dir.X * dir.Y, 0, 0,
2 * dir.X * dir.Y, 2 * dir.Y * dir.Y - 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
}
6.9.2 图元拾取(命中测试)
/// <summary>
/// 判断点击位置是否命中图元
/// </summary>
public static bool HitTest(LcEntity entity, Point2d clickPoint, double tolerance)
{
return entity.DistanceTo(clickPoint) <= tolerance;
}
/// <summary>
/// 窗口选择(全包含)
/// </summary>
public static IEnumerable<LcEntity> WindowSelect(
IEnumerable<LcEntity> entities, BoundingBox2d window)
{
return entities.Where(e =>
window.Contains(e.Bounds.Min) &&
window.Contains(e.Bounds.Max));
}
/// <summary>
/// 交叉选择(相交即选中)
/// </summary>
public static IEnumerable<LcEntity> CrossingSelect(
IEnumerable<LcEntity> entities, BoundingBox2d window)
{
return entities.Where(e => e.Bounds.Intersects(window));
}
6.10 本章小结
本章详细介绍了LightCAD的二维图元系统,包括直线、圆弧、圆、椭圆、多段线、多边形和样条曲线七种基本图元。每种图元都有明确的数据结构定义、多种创建方式和丰富的几何操作方法。多段线通过凸度(Bulge)机制实现了直线段和圆弧段的混合表示,是CAD中最灵活的图元类型。所有图元通过统一的变换接口支持平移、旋转、缩放和镜像操作。
上一章:第五章:核心数据模型详解
下一章:第七章:三维图元系统