第十八章:标注与文本系统
18.1 标注系统概述
18.1.1 标注的重要性
标注是工程图纸中不可或缺的组成部分,用于标明尺寸、角度、半径等几何信息。LightCAD提供了完整的二维和三维标注系统。
18.1.2 标注类型
二维标注(LightCAD.Core/Elements/Dim/):
| 标注类型 | 说明 |
|---|---|
| LcDimLinear | 线性标注(水平、垂直、对齐) |
| LcDimAngular | 角度标注 |
| LcDimRadial | 半径标注 |
| LcDimDiameter | 直径标注 |
| LcDimOrdinate | 坐标标注 |
| LcDimArcLength | 弧长标注 |
三维标注(LightCAD.Core/Elements/Dim3/):
| 标注类型 | 说明 |
|---|---|
| LcDim3dLinear | 三维线性标注 |
| LcDim3dAngular | 三维角度标注 |
| LcDim3dRadial | 三维半径标注 |
| LcDim3dElevation | 标高标注 |
18.2 标注基类
18.2.1 标注数据结构
public abstract class LcDimension : LcEntity, IUpdateObject
{
/// <summary>
/// 标注样式
/// </summary>
public DimStyle Style { get; set; }
/// <summary>
/// 标注文本(null表示使用测量值)
/// </summary>
public string OverrideText { get; set; }
/// <summary>
/// 文本位置
/// </summary>
public Point2d TextPosition { get; set; }
/// <summary>
/// 文本旋转角度
/// </summary>
public double TextRotation { get; set; }
/// <summary>
/// 测量值
/// </summary>
public abstract double MeasuredValue { get; }
/// <summary>
/// 获取显示文本
/// </summary>
public string DisplayText
{
get
{
if (!string.IsNullOrEmpty(OverrideText))
return OverrideText;
return FormatMeasuredValue();
}
}
/// <summary>
/// 格式化测量值
/// </summary>
protected string FormatMeasuredValue()
{
var value = MeasuredValue;
var precision = Style?.Precision ?? 2;
var prefix = Style?.Prefix ?? "";
var suffix = Style?.Suffix ?? "";
return $"{prefix}{value.ToString($"F{precision}")}{suffix}";
}
public override string TypeName => "Dimension";
}
18.2.2 标注样式
public class DimStyle
{
/// <summary>
/// 样式名称
/// </summary>
public string Name { get; set; }
// 尺寸线设置
public LcColor DimensionLineColor { get; set; } = LcColor.ByBlock;
public double DimensionLineWeight { get; set; } = 0.25;
public double DimensionLineExtension { get; set; } = 2;
// 延伸线设置
public LcColor ExtensionLineColor { get; set; } = LcColor.ByBlock;
public double ExtensionLineOffset { get; set; } = 2;
public double ExtensionLineExtension { get; set; } = 2;
public bool SuppressExtLine1 { get; set; } = false;
public bool SuppressExtLine2 { get; set; } = false;
// 箭头设置
public ArrowType ArrowType { get; set; } = ArrowType.ClosedFilled;
public double ArrowSize { get; set; } = 3;
// 文本设置
public string TextFont { get; set; } = "SimSun";
public double TextHeight { get; set; } = 3.5;
public LcColor TextColor { get; set; } = LcColor.ByBlock;
public TextAlignment TextAlignment { get; set; } = TextAlignment.Center;
public double TextGap { get; set; } = 1;
public int Precision { get; set; } = 2;
public string Prefix { get; set; } = "";
public string Suffix { get; set; } = "";
// 比例设置
public double ScaleFactor { get; set; } = 1.0;
// 公差设置
public bool ShowTolerance { get; set; } = false;
public double ToleranceUpper { get; set; } = 0;
public double ToleranceLower { get; set; } = 0;
}
public enum ArrowType
{
ClosedFilled, // 实心箭头
ClosedEmpty, // 空心箭头
Open, // 开放箭头
Dot, // 圆点
Slash, // 斜线
None // 无
}
public enum TextAlignment
{
Center, // 居中
Above, // 上方
Outside // 外侧
}
18.3 线性标注(LcDimLinear)
18.3.1 数据结构
public class LcDimLinear : LcDimension
{
/// <summary>
/// 第一个定义点(被标注对象的端点1)
/// </summary>
public Point2d DefPoint1 { get; set; }
/// <summary>
/// 第二个定义点(被标注对象的端点2)
/// </summary>
public Point2d DefPoint2 { get; set; }
/// <summary>
/// 尺寸线位置
/// </summary>
public Point2d DimLinePoint { get; set; }
/// <summary>
/// 标注方向
/// </summary>
public DimLinearType LinearType { get; set; } = DimLinearType.Aligned;
/// <summary>
/// 强制角度(用于旋转标注)
/// </summary>
public double? ForceAngle { get; set; }
/// <summary>
/// 测量值
/// </summary>
public override double MeasuredValue
{
get
{
switch (LinearType)
{
case DimLinearType.Horizontal:
return Math.Abs(DefPoint2.X - DefPoint1.X);
case DimLinearType.Vertical:
return Math.Abs(DefPoint2.Y - DefPoint1.Y);
case DimLinearType.Aligned:
return DefPoint1.DistanceTo(DefPoint2);
case DimLinearType.Rotated:
if (ForceAngle.HasValue)
{
var dir = new Vector2d(
Math.Cos(ForceAngle.Value),
Math.Sin(ForceAngle.Value));
var diff = DefPoint2 - DefPoint1;
return Math.Abs(diff.Dot(dir));
}
return DefPoint1.DistanceTo(DefPoint2);
default:
return DefPoint1.DistanceTo(DefPoint2);
}
}
}
/// <summary>
/// 获取标注的几何图形(用于渲染)
/// </summary>
public DimGeometry GetGeometry()
{
var geometry = new DimGeometry();
var style = Style ?? DimStyle.Default;
// 计算尺寸线方向
Vector2d dimDirection;
switch (LinearType)
{
case DimLinearType.Horizontal:
dimDirection = Vector2d.XAxis;
break;
case DimLinearType.Vertical:
dimDirection = Vector2d.YAxis;
break;
default:
dimDirection = (DefPoint2 - DefPoint1).Normalize();
break;
}
var perpDirection = dimDirection.Perpendicular();
// 将定义点投影到尺寸线上
var dimLineY = (DimLinePoint - DefPoint1).Dot(perpDirection);
var ext1End = DefPoint1 + perpDirection * dimLineY;
var ext2End = DefPoint2 + perpDirection * dimLineY;
// 延伸线
var ext1Start = DefPoint1 + perpDirection *
Math.Sign(dimLineY) * style.ExtensionLineOffset;
var ext2Start = DefPoint2 + perpDirection *
Math.Sign(dimLineY) * style.ExtensionLineOffset;
geometry.ExtensionLines.Add((ext1Start,
ext1End + perpDirection * Math.Sign(dimLineY) *
style.ExtensionLineExtension));
geometry.ExtensionLines.Add((ext2Start,
ext2End + perpDirection * Math.Sign(dimLineY) *
style.ExtensionLineExtension));
// 尺寸线
geometry.DimensionLine = (ext1End, ext2End);
// 箭头
geometry.Arrow1Position = ext1End;
geometry.Arrow2Position = ext2End;
geometry.ArrowDirection = dimDirection;
// 文本位置
geometry.TextPosition = ext1End.MidPoint(ext2End);
geometry.Text = DisplayText;
return geometry;
}
}
public enum DimLinearType
{
Horizontal, // 水平标注
Vertical, // 垂直标注
Aligned, // 对齐标注
Rotated // 旋转标注
}
18.4 角度标注
public class LcDimAngular : LcDimension
{
public Point2d Vertex { get; set; }
public Point2d Point1 { get; set; }
public Point2d Point2 { get; set; }
public Point2d ArcPoint { get; set; }
public override double MeasuredValue
{
get
{
var dir1 = (Point1 - Vertex).Normalize();
var dir2 = (Point2 - Vertex).Normalize();
var angle = Math.Acos(dir1.Dot(dir2));
return AngleUtils.RadToDeg(angle);
}
}
protected override string FormatMeasuredValue()
{
return $"{MeasuredValue.ToString($"F{Style?.Precision ?? 1}")}°";
}
}
18.5 半径和直径标注
public class LcDimRadial : LcDimension
{
public Point2d Center { get; set; }
public Point2d ChordPoint { get; set; }
public override double MeasuredValue =>
Center.DistanceTo(ChordPoint);
protected override string FormatMeasuredValue()
{
var value = MeasuredValue;
return $"R{value.ToString($"F{Style?.Precision ?? 2}")}";
}
}
public class LcDimDiameter : LcDimension
{
public Point2d Center { get; set; }
public Point2d ChordPoint { get; set; }
public override double MeasuredValue =>
Center.DistanceTo(ChordPoint) * 2;
protected override string FormatMeasuredValue()
{
var value = MeasuredValue;
return $"⌀{value.ToString($"F{Style?.Precision ?? 2}")}";
}
}
18.6 文本系统
18.6.1 文本实体
public class LcText : LcEntity, IUpdateObject
{
/// <summary>
/// 文本内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 插入点
/// </summary>
public Point2d InsertionPoint { get; set; }
/// <summary>
/// 文字高度
/// </summary>
public double Height { get; set; } = 3.5;
/// <summary>
/// 旋转角度
/// </summary>
public double Rotation { get; set; } = 0;
/// <summary>
/// 宽度因子
/// </summary>
public double WidthFactor { get; set; } = 1.0;
/// <summary>
/// 倾斜角度
/// </summary>
public double ObliqueAngle { get; set; } = 0;
/// <summary>
/// 字体名称
/// </summary>
public string FontName { get; set; } = "SimSun";
/// <summary>
/// 水平对齐方式
/// </summary>
public TextHorizontalAlignment HorizontalAlignment { get; set; }
= TextHorizontalAlignment.Left;
/// <summary>
/// 垂直对齐方式
/// </summary>
public TextVerticalAlignment VerticalAlignment { get; set; }
= TextVerticalAlignment.Baseline;
/// <summary>
/// 是否为多行文本
/// </summary>
public bool IsMultiline { get; set; } = false;
/// <summary>
/// 多行文本宽度
/// </summary>
public double TextWidth { get; set; }
public override string TypeName => "Text";
public override BoundingBox2d Bounds
{
get
{
var textWidth = CalculateTextWidth();
var textHeight = Height;
// 考虑旋转
if (Math.Abs(Rotation) < 1e-10)
{
return new BoundingBox2d
{
Min = InsertionPoint,
Max = new Point2d(
InsertionPoint.X + textWidth,
InsertionPoint.Y + textHeight)
};
}
else
{
// 计算旋转后的包围盒
var corners = GetRotatedCorners(textWidth, textHeight);
return BoundingBox2d.FromPoints(corners);
}
}
}
/// <summary>
/// 计算文本宽度
/// </summary>
private double CalculateTextWidth()
{
var font = FontManager.GetFont(FontName);
if (font == null) return Content.Length * Height * 0.6;
double width = 0;
foreach (var ch in Content)
{
var charWidth = font.GetCharWidth(ch);
width += charWidth * Height * WidthFactor;
}
return width;
}
/// <summary>
/// 获取文本轮廓(用于渲染)
/// </summary>
public List<List<Point2d>> GetOutlines()
{
var font = FontManager.GetFont(FontName);
var outlines = new List<List<Point2d>>();
var currentX = 0.0;
foreach (var ch in Content)
{
var charOutline = font.GetCharOutline(ch);
if (charOutline != null)
{
var transformed = charOutline.Select(p =>
{
var scaled = new Point2d(
p.X * Height * WidthFactor + currentX,
p.Y * Height);
// 应用旋转
if (Math.Abs(Rotation) > 1e-10)
{
var rotated = scaled.ToVector().Rotate(Rotation);
scaled = new Point2d(rotated.X, rotated.Y);
}
// 应用插入点偏移
return new Point2d(
scaled.X + InsertionPoint.X,
scaled.Y + InsertionPoint.Y);
}).ToList();
outlines.Add(transformed);
}
currentX += font.GetCharWidth(ch) * Height * WidthFactor;
}
return outlines;
}
public bool NeedsUpdate { get; private set; }
public void OnUpdate(UpdateContext context) { NeedsUpdate = false; }
}
public enum TextHorizontalAlignment
{
Left,
Center,
Right
}
public enum TextVerticalAlignment
{
Baseline,
Bottom,
Middle,
Top
}
18.7 表格系统
18.7.1 表格实体
public class LcTable : LcEntity, IUpdateObject
{
public Point2d InsertionPoint { get; set; }
public int RowCount { get; set; }
public int ColumnCount { get; set; }
public double[] RowHeights { get; set; }
public double[] ColumnWidths { get; set; }
public TableCell[,] Cells { get; set; }
public TableStyle Style { get; set; }
public override string TypeName => "Table";
/// <summary>
/// 获取指定单元格
/// </summary>
public TableCell GetCell(int row, int col)
{
return Cells[row, col];
}
/// <summary>
/// 设置单元格内容
/// </summary>
public void SetCellContent(int row, int col, string content)
{
Cells[row, col].Content = content;
}
/// <summary>
/// 计算表格总宽度
/// </summary>
public double TotalWidth => ColumnWidths.Sum();
/// <summary>
/// 计算表格总高度
/// </summary>
public double TotalHeight => RowHeights.Sum();
public bool NeedsUpdate { get; private set; }
public void OnUpdate(UpdateContext context) { NeedsUpdate = false; }
}
public class TableCell
{
public string Content { get; set; }
public TextHorizontalAlignment Alignment { get; set; }
public LcColor TextColor { get; set; }
public LcColor BackgroundColor { get; set; }
public double TextHeight { get; set; }
public int MergeRight { get; set; } = 0;
public int MergeDown { get; set; } = 0;
}
18.8 SHX字体解析
18.8.1 SHX字体格式
SHX(Shape eXtension)是AutoCAD专用的矢量字体格式:
public class ShxFont
{
private Dictionary<char, ShxGlyph> glyphs = new();
public string Name { get; set; }
public double Above { get; set; }
public double Below { get; set; }
/// <summary>
/// 加载SHX文件
/// </summary>
public static ShxFont Load(string filePath)
{
var font = new ShxFont();
font.Name = Path.GetFileNameWithoutExtension(filePath);
using var stream = File.OpenRead(filePath);
using var reader = new BinaryReader(stream);
// 读取文件头
var header = reader.ReadBytes(32);
font.Above = reader.ReadDouble();
font.Below = reader.ReadDouble();
// 读取字形定义
while (stream.Position < stream.Length)
{
var charCode = reader.ReadUInt16();
var glyphData = ReadGlyphData(reader);
font.glyphs[(char)charCode] = glyphData;
}
return font;
}
/// <summary>
/// 获取字符的轮廓点
/// </summary>
public List<Point2d> GetCharOutline(char ch)
{
if (glyphs.TryGetValue(ch, out var glyph))
{
return glyph.Points;
}
return null;
}
/// <summary>
/// 获取字符宽度
/// </summary>
public double GetCharWidth(char ch)
{
if (glyphs.TryGetValue(ch, out var glyph))
{
return glyph.Width;
}
return 0.6; // 默认宽度
}
}
18.9 本章小结
本章全面介绍了LightCAD的标注和文本系统。标注系统支持线性、角度、半径、直径等多种标注类型,通过标注样式(DimStyle)统一管理标注的外观。文本系统支持单行和多行文本,通过TTF和SHX字体渲染。表格系统支持创建和编辑工程表格。SHX字体解析器确保了与AutoCAD字体的兼容性。
上一章:第十七章:数据库与项目管理
下一章:第十九章:调试测试与性能优化