场布元素实现详解
第六章:场布元素实现详解
6.1 场布元素概述
6.1.1 元素分类
FY_Layout的场布元素按功能可分为以下几类:
| 分类 | 元素 | 说明 |
|---|---|---|
| 区域类 | 草坪、场地、用地红线 | 闭合多边形区域 |
| 坑槽类 | 基坑、土方回填 | 带标高和放坡的区域 |
| 道路类 | 出土道路、城市道路、硬化地面 | 线性或带状区域 |
| 设施类 | 围栏、防护栏杆 | 线性安全设施 |
| 建筑类 | 拟建建筑、板房 | 建筑物表示 |
6.1.2 通用实现模式
每个场布元素都遵循以下实现模式:
QdXxx.cs - 元素数据类
QdXxxDef.cs - 元素定义类
XxxAction.cs - 二维操作类(绘制、编辑、显示)
Xxx3dAction.cs - 三维操作类(三维模型生成)
6.2 草坪元素(Lawn)
6.2.1 功能特点
- 支持任意多边形绘制
- 支持矩形快速绘制
- 支持现有多段线转换
- 显示草坪填充图案
- 可设置底部标高
6.2.2 LawnAction详解
namespace QdLayout
{public class LawnAction : DirectComponentAction{// 创建方法定义private static readonly LcCreateMethod[] CreateMethods;private PointInputer pointInputer;private CmdTextInputer cmdTextInputer;private LcPolyLine OutLoop;public LawnAction() { }public LawnAction(IDocumentEditor docEditor) : base(docEditor){commandCtrl.WriteInfo("命令:Lawn");}static LawnAction(){CreateMethods = new LcCreateMethod[1];CreateMethods[0] = new LcCreateMethod(){Name = "CreateLawn",Description = "创建草坪",Steps = new LcCreateStep[]{new LcCreateStep { Name = "Step0", Options = "指定轮廓第一个点:" },new LcCreateStep { Name = "Step1", Options = "指定轮廓下一点或 [结束(E)]" },}};}/// <summary>/// 任意多边形绘制/// </summary>public async void ExecCreatePoly(string[] args = null){OutLoop = null;commandCtrl.WriteInfo("绘制草坪轮廓中...");var plAc = new PolyLineAction(docEditor);await plAc.StartCreating();if (!await plAc.OtherActionCreating()){goto End;}else{OutLoop = plAc.CurrentPoly;}CreateLawn();End:if (OutLoop != null){vportRt.ActiveElementSet.RemoveElement(OutLoop);}plAc.EndCreating();EndCreating();}/// <summary>/// 矩形绘制/// </summary>public async void ExecCreateRec(string[] args = null){EndCreating();OutLoop = null;commandCtrl.WriteInfo("绘制草坪轮廓中...");var plAc = new PolyLineAction(docEditor);await plAc.StartCreating();if (!await plAc.OtherActionCreatingRect()){goto End;}else{OutLoop = plAc.CurrentPoly;}CreateLawn();End:if (OutLoop != null){vportRt.ActiveElementSet.RemoveElement(OutLoop);}plAc.EndCreating();EndCreating();}/// <summary>/// 从现有线段转换/// </summary>public async void ExecCreate(string[] args = null){var elementInputer = new ElementSetInputer(this.docEditor);Step0:var result = await elementInputer.Execute("请选择已有闭合线段创建草坪:");if (elementInputer.isCancelled || result == null){Cancel();return;}if (result.ValueX != null){var eles = result.ValueX as List<LcElement>;var lines = new List<LcCurve2d>();foreach (var ele in eles){if (ele is LcLine line) lines.Add(line);else if (ele is LcPolyLine polyLine) lines.Add(polyLine);else if (ele is LcArc arc) lines.Add(arc);}// 检查闭合环var polys = LcCurveChangeLoop.CheckLoops(lines);foreach (var line in polys){OutLoop = line;CreateLawn();}}else if (result.Option != null){return;}else{goto Step0;}}/// <summary>/// 创建草坪元素/// </summary>public void CreateLawn(){var doc = docRt.Document;// 获取组件定义var lawnDef = docRt.GetUseComDef($"{NamespaceKey}.绿色文明", "草坪", null) as QdLawnDef;// 创建草坪实例var lawn = new QdLawn(lawnDef);lawn.Initilize(doc);// 设置轮廓var poly = OutLoop.Clone() as LcPolyLine;lawn.Outline = poly.Curve.Clone() as Polyline2d;lawn.ResetBoundingBox();// 设置属性lawn.Layer = GetLayer().Name;lawn.Bottom = 0;lawn.Material = MaterialManager.GetMaterial(MaterialManager.LawnUuid);// 插入到文档vportRt.ActiveElementSet.InsertElement(lawn);docRt.Action.ClearSelects();}/// <summary>/// 绘制草坪(二维显示)/// </summary>public override void Draw(LcCanvas2d canvas, LcElement element, Matrix3 matrix){var lawn = element as QdLawn;var pen = GetDrawPen(lawn);DrawLawn(canvas, lawn, matrix, pen);}private void DrawLawn(LcCanvas2d canvas, QdLawn lawn, Matrix3 matrix, LcPaint pen){// 绘制轮廓线foreach (var curve in lawn.GetShapes()[0].Curve2ds){canvas.DrawCurve(pen, curve, matrix);}// 绘制文字标注var textPaint = new LcTextPaint{Color = new Color().Set(GetLayer().Color),FontName = "仿宋",FontName2 = "仿宋",Size = 800,WordSpace = 5,WidthFactor = 1};textPaint.Position = lawn.BoundingBox.Center;canvas.DrawText(textPaint, "草坪", new Matrix3(), out var charBoxs);}/// <summary>/// 获取控制夹点/// </summary>public override ControlGrip[] GetControlGrips(LcElement element){var lawn = element as QdLawn;var grips = new List<ControlGrip>();// 中心夹点(用于移动)grips.Add(new ControlGrip{Element = lawn,Name = "Center",Position = lawn.BoundingBox.Center.Clone()});// 轮廓顶点夹点(用于编辑形状)var points = lawn.GetShapes()[0].Curve2ds.Select(n => n.GetPoints(1)[0]).ToList();for (int i = 0; i < points.Count; i++){grips.Add(new ControlGrip{Element = lawn,Name = $"Outline_{i}",Position = points[i]});}return grips.ToArray();}/// <summary>/// 处理夹点拖动/// </summary>public override void SetDragGrip(LcElement element, ControlGrip grip, Vector2 position, bool isEnd){var lawn = element as QdLawn;if (isEnd){if (grip.Name == "Center"){// 移动整个元素var offset = position - grip.Position;lawn.Translate(offset);}else if (grip.Name.StartsWith("Outline")){// 编辑轮廓点var idx = int.Parse(grip.Name.Split('_')[1]);var poly = lawn.Outline.Clone() as Polyline2d;var offset = position - grip.Position;(poly.Curve2ds[idx] as Line2d).Start.Add(offset);if (idx == 0)(poly.Curve2ds.Last() as Line2d).End.Add(offset);else(poly.Curve2ds[idx - 1] as Line2d).End.Add(offset);lawn.Outline = poly;}}}/// <summary>/// 获取或创建图层/// </summary>private LcLayer GetLayer(){var layer = docRt.Document.Layers.FirstOrDefault(n => n.Name == "Layout_Lawn");if (layer == null){layer = docRt.Document.CreateObject<LcLayer>();layer.Name = "Layout_Lawn";layer.Color = 0x00FF00; // 绿色layer.SetLineType(new LcLineType("ByLayer"));layer.Transparency = 0;docRt.Document.Layers.Add(layer);}return layer;}}
}
6.3 基坑元素(FoundationPit)
6.3.1 功能特点
- 支持放坡计算
- 可设置底标高和顶标高
- 支持向内/向外放坡
- 自动生成坡面图案
6.3.2 FoundationPitAction关键代码
public class FoundationPitAction : DirectComponentAction
{public void CreateFoundationPit(){var doc = docRt.Document;// 获取基坑定义var fdPitDef = docRt.GetUseComDef($"{NamespaceKey}.土方基坑", "基坑", null) as QdFoundationPitDef;// 创建基坑实例var fdPit = new QdFoundationPit(fdPitDef);fdPit.Initilize(doc);// 设置轮廓var poly = OutLoop.Clone() as LcPolyLine;fdPit.Outline = poly.Curve.Clone() as Polyline2d;// 设置默认参数fdPit.Pattern = 0; // 向外放坡fdPit.Bottom = -4000; // 底标高-4米fdPit.Elevation = 0; // 顶标高0fdPit.Factor = 0.4; // 放坡系数0.4fdPit.ResetBoundingBox();fdPit.Layer = GetLayer().Name;vportRt.ActiveElementSet.InsertElement(fdPit);docRt.Action.ClearSelects();}/// <summary>/// 获取属性编辑器/// </summary>public override List<PropertyObserver> GetPropertyObservers(){return new List<PropertyObserver>{new PropertyObserver{Name = "Bottom",DisplayName = "土方底绝对标高",CategoryName = "Geometry",CategoryDisplayName = "几何图形",PropType = PropertyType.Double,Getter = (ele) => (ele as QdFoundationPit).Bottom,Setter = (ele, value) =>{if (double.TryParse(value.ToString(), out var bottom))(ele as QdFoundationPit).Bottom = bottom;}},new PropertyObserver{Name = "Elevation",DisplayName = "土方顶绝对标高",PropType = PropertyType.Double,Getter = (ele) => (ele as QdFoundationPit).Elevation,Setter = (ele, value) =>{if (double.TryParse(value.ToString(), out var elevation))(ele as QdFoundationPit).Elevation = elevation;}},new PropertyObserver{Name = "Factor",DisplayName = "放坡系数",PropType = PropertyType.Double,Getter = (ele) => (ele as QdFoundationPit).Factor,Setter = (ele, value) =>{if (double.TryParse(value.ToString(), out var factor))(ele as QdFoundationPit).Factor = factor;}},new PropertyObserver{Name = "Pattern",DisplayName = "放坡方式",PropType = PropertyType.Array,Source = (ele) => new string[] { "向外放坡", "向内放坡" },Getter = (ele) => (ele as QdFoundationPit).Pattern == 0 ? "向外放坡" : "向内放坡",Setter = (ele, value) =>{var pattern = value?.ToString() == "向外放坡" ? 0 : 1;(ele as QdFoundationPit).Pattern = pattern;}}};}
}
6.4 围栏元素(Fence)
6.4.1 功能特点
- 沿路径绘制围栏
- 支持自定义围栏样式
- 显示围栏立柱和横杆
- 支持门的设置
6.4.2 QdFence元素类
public class QdFence : DirectComponent
{/// <summary>/// 路径/// </summary>public Polyline2d Path{get => BaseCurve as Polyline2d;set => BaseCurve = value;}/// <summary>/// 围栏高度/// </summary>public double Height{get => Properties.GetValue<double>("Height");set => SetProps((GetPropId(nameof(Height)), value));}/// <summary>/// 立柱间距/// </summary>public double PostSpacing{get => Properties.GetValue<double>("PostSpacing");set => SetProps((GetPropId(nameof(PostSpacing)), value));}/// <summary>/// 门位置列表/// </summary>public List<FenceGate> Gates { get; set; } = new List<FenceGate>();public QdFence(QdFenceDef def) : base(def){Type = LayoutElementType.Fence;Path = new Polyline2d();Height = 2000; // 默认2米高PostSpacing = 3000; // 默认3米间距}
}public class FenceGate
{public double Position { get; set; } // 沿路径的位置public double Width { get; set; } // 门宽度public string Type { get; set; } // 门类型
}
6.5 出土道路(Berm)
6.5.1 功能特点
- 双向道路绘制
- 支持道路宽度设置
- 自动生成路肩
- 计算运输长度
6.5.2 QdBerm元素类
public class QdBerm : DirectComponent
{/// <summary>/// 道路中心线/// </summary>public Polyline2d CenterLine{get => BaseCurve as Polyline2d;set => BaseCurve = value;}/// <summary>/// 道路宽度/// </summary>public double Width{get => Properties.GetValue<double>("Width");set{Properties.SetValue("Width", value);ResetCache();}}/// <summary>/// 道路长度(计算属性)/// </summary>public double Length{get{double totalLength = 0;foreach (var curve in CenterLine.Curve2ds){totalLength += curve.Length;}return totalLength;}}/// <summary>/// 获取道路边界/// </summary>public override Curve2dGroupCollection GetShapes(){var shapes = new Curve2dGroupCollection();// 生成左边界var leftBoundary = OffsetCurve(CenterLine, Width / 2);// 生成右边界var rightBoundary = OffsetCurve(CenterLine, -Width / 2);var group = new Curve2dGroup();group.Curve2ds.AddRange(leftBoundary);group.Curve2ds.AddRange(rightBoundary);shapes.Add(group);return shapes;}private List<Curve2d> OffsetCurve(Polyline2d poly, double offset){// 曲线偏移算法实现var result = new List<Curve2d>();foreach (var curve in poly.Curve2ds){if (curve is Line2d line){var dir = line.Dir.Clone().RotateAround(new Vector2(), -Math.PI / 2);var newLine = new Line2d(line.Start.Clone().AddScaledVector(dir, offset),line.End.Clone().AddScaledVector(dir, offset));result.Add(newLine);}}return result;}
}
6.6 拟建建筑(PlanBuild)
6.6.1 功能特点
- 绘制建筑平面轮廓
- 设置建筑高度
- 显示建筑名称
- 支持多层建筑
6.6.2 QdPlanBuild元素类
public class QdPlanBuild : DirectComponent
{/// <summary>/// 建筑轮廓/// </summary>public Polyline2d Outline{get => BaseCurve as Polyline2d;set => BaseCurve = value;}/// <summary>/// 建筑名称/// </summary>public string BuildingName{get => Properties.GetValue<string>("BuildingName");set => SetProps((GetPropId(nameof(BuildingName)), value));}/// <summary>/// 建筑高度/// </summary>public double Height{get => Properties.GetValue<double>("Height");set => SetProps((GetPropId(nameof(Height)), value));}/// <summary>/// 层数/// </summary>public int FloorCount{get => Properties.GetValue<int>("FloorCount");set => SetProps((GetPropId(nameof(FloorCount)), value));}/// <summary>/// 底部标高/// </summary>public double BaseElevation{get => Properties.GetValue<double>("BaseElevation");set => SetProps((GetPropId(nameof(BaseElevation)), value));}public QdPlanBuild(QdPlanBuildDef def) : base(def){Type = LayoutElementType.PlanBuild;Outline = new Polyline2d();BuildingName = "建筑";Height = 30000; // 默认30米FloorCount = 10; // 默认10层BaseElevation = 0;}/// <summary>/// 计算建筑面积/// </summary>public double GetArea(){// 使用多边形面积公式var points = Outline.Curve2ds.Select(c => c.GetPoints(1)[0]).ToList();double area = 0;for (int i = 0; i < points.Count; i++){var j = (i + 1) % points.Count;area += points[i].X * points[j].Y;area -= points[j].X * points[i].Y;}return Math.Abs(area) / 2;}
}
6.7 用地红线(PropertyLine)
6.7.1 功能特点
- 显示用地边界
- 特殊的红线样式
- 支持面积计算
- 作为其他元素的约束边界
6.7.2 PropertyLineAction绘制逻辑
public class PropertyLineAction : DirectComponentAction
{public override void Draw(LcCanvas2d canvas, LcElement element, Matrix3 matrix){var propLine = element as QdPropertyLine;// 使用红色虚线样式var pen = new LcPaint{Color = new Color(0xFF0000), // 红色Width = 2,StrokeStyle = StrokeStyle.Dashed,DashPattern = new float[] { 10, 5 }};// 绘制边界线foreach (var curve in propLine.GetShapes()[0].Curve2ds){canvas.DrawCurve(pen, curve, matrix);}// 绘制顶点标记var vertexPen = new LcPaint{Color = new Color(0xFF0000),Width = 1};var points = propLine.Outline.Curve2ds.Select(c => c.GetPoints(1)[0]).ToList();foreach (var point in points){canvas.DrawCircle(vertexPen, point.ApplyMatrix3(matrix), 50);}}
}
6.8 曲线闭合工具
6.8.1 LcCurveChangeLoop类
这是一个工具类,用于将多条分散的线段转换为闭合多边形:
public static class LcCurveChangeLoop
{/// <summary>/// 检查并生成闭合环/// </summary>public static List<LcPolyLine> CheckLoops(List<LcCurve2d> curves){var result = new List<LcPolyLine>();// 查找所有可能的闭合环var loops = FindClosedLoops(curves);foreach (var loop in loops){var polyline = new LcPolyLine();foreach (var curve in loop){polyline.AddCurve(curve);}polyline.IsClosed = true;result.Add(polyline);}return result;}private static List<List<Curve2d>> FindClosedLoops(List<LcCurve2d> curves){var loops = new List<List<Curve2d>>();var used = new HashSet<int>();for (int i = 0; i < curves.Count; i++){if (used.Contains(i)) continue;var loop = TryBuildLoop(curves, i, used);if (loop != null && loop.Count >= 3){loops.Add(loop);}}return loops;}private static List<Curve2d> TryBuildLoop(List<LcCurve2d> curves, int startIdx, HashSet<int> used){var loop = new List<Curve2d>();var startCurve = curves[startIdx].Curve;var currentEnd = startCurve.GetEndPoint();var targetStart = startCurve.GetStartPoint();loop.Add(startCurve.Clone());used.Add(startIdx);const double tolerance = 0.001;int maxIterations = curves.Count;int iterations = 0;while (iterations++ < maxIterations){// 检查是否闭合if (currentEnd.DistanceTo(targetStart) < tolerance){return loop;}// 查找下一条曲线bool found = false;for (int i = 0; i < curves.Count; i++){if (used.Contains(i)) continue;var curve = curves[i].Curve;// 检查起点连接if (curve.GetStartPoint().DistanceTo(currentEnd) < tolerance){loop.Add(curve.Clone());used.Add(i);currentEnd = curve.GetEndPoint();found = true;break;}// 检查终点连接(需要反向)if (curve.GetEndPoint().DistanceTo(currentEnd) < tolerance){var reversed = curve.Clone();reversed.Reverse();loop.Add(reversed);used.Add(i);currentEnd = reversed.GetEndPoint();found = true;break;}}if (!found) break;}return null;}
}
6.9 图层管理
6.9.1 场布元素图层规范
| 元素类型 | 图层名称 | 颜色 |
|---|---|---|
| 草坪 | Layout_Lawn | 0x00FF00(绿色) |
| 基坑 | Layout_FoundationPit | 0xFFFF00(黄色) |
| 围栏 | Layout_Fence | 0x0000FF(蓝色) |
| 出土道路 | Layout_Berm | 0x808080(灰色) |
| 拟建建筑 | Layout_PlanBuild | 0x00FFFF(青色) |
| 用地红线 | Layout_PropertyLine | 0xFF0000(红色) |
6.9.2 图层管理通用代码
/// <summary>
/// 图层管理基类
/// </summary>
public abstract class LayoutLayerManager
{protected LcDocument Document { get; }protected LayoutLayerManager(LcDocument document){Document = document;}/// <summary>/// 获取或创建图层/// </summary>protected LcLayer GetOrCreateLayer(string name, int color, string lineType = "ByLayer"){var layer = Document.Layers.FirstOrDefault(l => l.Name == name);if (layer == null){layer = Document.CreateObject<LcLayer>();layer.Name = name;layer.Color = color;layer.SetLineType(new LcLineType(lineType));layer.Transparency = 0;Document.Layers.Add(layer);}return layer;}
}
6.10 本章小结
本章详细介绍了FY_Layout中各种场布元素的实现:
- 草坪元素:任意多边形绘制、矩形绘制、线段转换
- 基坑元素:放坡计算、标高设置、属性编辑
- 围栏元素:沿路径绘制、门的设置
- 出土道路:道路宽度、边界生成
- 拟建建筑:建筑属性、面积计算
- 用地红线:特殊样式、边界约束
- 曲线闭合工具:线段转换为闭合多边形
- 图层管理:规范的图层命名和颜色
下一章我们将学习板房系统的开发,这是FY_Layout中最复杂的功能模块。
← 上一章目录下一章 →