第十三章:道路与交叉口系统详解
13.1 道路系统概述
13.1.1 道路系统在场布设计中的地位
在建筑工程场地布置设计中,道路系统是连接各个功能区域的关键基础设施。FY_Layout的道路系统提供了从城市道路到施工便道、从路面硬化到出土运输等全方位的道路设计能力。
道路系统在FY_Layout中的核心地位体现在:
- 连通性:道路是场地内各功能区域(如板房区、材料堆场、基坑区域)的交通纽带
- 安全性:合理的道路布局是施工安全的重要保障
- 经济性:优化的道路设计可以减少土方运输成本
- 规范性:道路设计需要满足施工规范和安全标准
13.1.2 道路系统的元素类型
FY_Layout的道路系统包含四种主要元素类型:
| 元素类型 | 类名 | 中文名 | 主要用途 |
|---|---|---|---|
| Road | QdRoad | 硬化地面 | 城市道路和主要交通干道 |
| Berm | QdBerm | 出土道路 | 土方运输便道 |
| Harden | QdHarden | 路面硬化 | 施工场地路面硬化处理 |
| Ground | QdGround | 硬化地面 | 场地硬化区域 |
这四种元素各有侧重,但共享相似的开发模式和技术架构。
13.1.3 道路系统的技术特点
FY_Layout道路系统具有以下技术特点:
- 多种创建方式:支持多边形绘制、矩形绘制、已有线段转换三种创建模式
- 参数化设计:道路宽度、高程、材质等参数可动态调整
- 二三维联动:二维绘图自动生成对应的三维模型
- 交叉口处理:支持道路交叉口的自动计算和渲染
- 图层管理:每种道路类型自动分配到对应图层
13.2 QdRoad 城市道路实现详解
13.2.1 QdRoad类结构
QdRoad是FY_Layout中最复杂的元素类之一,其源代码超过44KB,体现了城市道路设计的复杂性。
public class QdRoad : DirectComponent
{
// 道路底部高程
public double Bottom
{
get { return Properties.GetValue<double>("Bottom"); }
set { SetProps((GetPropId(nameof(Bottom)), value)); }
}
// 道路材质信息
public MaterialInfo Material
{
get { return Properties.GetValue<MaterialInfo>("Material"); }
set { SetProps((GetPropId(nameof(Material)), value)); }
}
// 道路轮廓线
public Polyline2d Outline
{
get { return this.BaseCurve as Polyline2d; }
set { this.BaseCurve = value; }
}
// 构造函数
public QdRoad(QdRoadDef roadDef) : base(roadDef)
{
Type = LayoutElementType.Road;
Outline = new Polyline2d();
}
// 计算包围盒
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds
.SelectMany(n => n.GetPoints()).ToArray());
}
// 克隆方法
public override LcElement Clone()
{
var clone = new QdRoad(Definition as QdRoadDef);
clone.Copy(this);
return clone;
}
// 复制属性
public override void Copy(LcElement src)
{
base.Copy(src);
var road = (QdRoad)src;
}
}
13.2.2 RoadAction操作类
RoadAction负责处理城市道路的用户交互操作:
public class RoadAction : DirectComponentAction
{
private PointInputer pointInputer;
private CmdTextInputer cmdTextInputer;
private LcPolyLine OutLoop;
public RoadAction() { }
public RoadAction(IDocumentEditor docEditor) : base(docEditor)
{
commandCtrl.WriteInfo("命令:Road");
}
// 创建道路
public async void ExecCreate(string[] args = null)
{
OutLoop = null;
commandCtrl.WriteInfo("绘制道路轮廓中...");
var doc = docRt.Document;
var plAc = new PolyLineAction(docEditor);
await plAc.StartCreating();
if (!await plAc.OtherActionCreating())
{
goto End;
}
else
{
OutLoop = plAc.CurrentPoly;
}
CreateRoad();
End:
if (OutLoop != null)
{
vportRt.ActiveElementSet.RemoveElement(OutLoop);
}
pointInputer = null;
cmdTextInputer = null;
plAc.EndCreating();
EndCreating();
}
// 创建道路元素
public void CreateRoad()
{
var doc = docRt.Document;
var roadDef = docRt.GetUseComDef(
$"{NamespaceKey}.道路工程", "道路", null) as QdRoadDef;
var road = new QdRoad(roadDef);
road.Initilize(doc);
var poly = OutLoop.Clone() as LcPolyLine;
road.Outline = poly.Curve.Clone() as Polyline2d;
road.ResetBoundingBox();
road.Layer = GetLayer().Name;
road.Bottom = 0;
road.Material = MaterialManager.GetMaterial(MaterialManager.ConcreteUuid);
vportRt.ActiveElementSet.InsertElement(road);
docRt.Action.ClearSelects();
}
}
13.2.3 道路命令注册
在LayoutCmds.cs中,道路命令的注册如下:
[CommandClass]
public class LayoutCmds
{
[CommandMethod(Name = "Road", ShortCuts = "ROD")]
public CommandResult DrawRoad(IDocumentEditor docEditor, string[] args)
{
var roadAction = new RoadAction(docEditor);
roadAction.ExecCreate(args);
return CommandResult.Succ();
}
}
13.2.4 道路的中心线与宽度计算
城市道路的核心几何计算涉及中心线偏移和宽度处理:
// 道路中心线偏移计算原理
// 1. 获取道路中心线的各段线段
// 2. 对每段线段进行法向偏移,得到左右边界
// 3. 处理弯道处的交点计算
// 4. 组合形成完整的道路轮廓
// 偏移算法核心(与基坑放坡类似)
public static List<Curve2d> ShapeExtend(List<Curve2d> curves, double width)
{
var newCurves = new List<Curve2d>();
for (var i = 0; i < curves.Count; i++)
{
var curve = curves[i].Clone();
if (curve is Line2d line)
{
// 对直线段进行法向偏移
line.Translate(
line.Dir.Clone()
.RotateAround(new Vector2(), -Math.PI / 2)
.MultiplyScalar(width));
newCurves.Add(line);
}
else if (curve is Arc2d arc)
{
// 对圆弧段调整半径
if (arc.IsClockwise)
arc.Radius -= width;
else
arc.Radius += width;
// 将圆弧离散为线段
var count = Convert.ToInt32(
Math.Abs((arc.EndAngle - arc.StartAngle) / Math.PI * 16));
count = Math.Max(5, count);
var ps = arc.GetPoints(count);
for (var k = 0; k < count; k++)
{
newCurves.Add(new Line2d(ps[k].Clone(), ps[k + 1].Clone()));
}
}
}
// 处理偏移后线段的交点
for (var i = 0; i < newCurves.Count; i++)
{
var lastCurve = newCurves[i == 0 ? newCurves.Count - 1 : i - 1];
var nextCurve = newCurves[i == newCurves.Count - 1 ? 0 : i + 1];
if (newCurves[i] is Line2d line)
{
if (lastCurve is Line2d lastLine)
{
var cps = Intersect2d.XLineWithXLine(
line.Start, line.Dir.Clone().Negate(),
lastLine.Start, lastLine.Dir);
if (cps != null) line.Start = cps;
}
if (nextCurve is Line2d nextLine)
{
var cpe = Intersect2d.XLineWithXLine(
line.Start, line.Dir,
nextLine.Start, nextLine.Dir);
if (cpe != null) line.End = cpe;
}
}
}
return newCurves;
}
13.3 出土道路(QdBerm)详解
13.3.1 QdBerm类定义
出土道路用于土方运输路线的设计,通常连接基坑与场外出口:
public class QdBerm : DirectComponent
{
// 底部高程
public double Bottom
{
get { return Properties.GetValue<double>("Bottom"); }
set { SetProps((GetPropId(nameof(Bottom)), value)); }
}
// 材质信息
public MaterialInfo Material
{
get { return Properties.GetValue<MaterialInfo>("Material"); }
set { SetProps((GetPropId(nameof(Material)), value)); }
}
// 道路轮廓
public Polyline2d Outline
{
get { return this.BaseCurve as Polyline2d; }
set { this.BaseCurve = value; }
}
public QdBerm(QdBermDef bermDef) : base(bermDef)
{
Type = LayoutElementType.Berm;
Outline = new Polyline2d();
}
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds
.SelectMany(n => n.GetPoints()).ToArray());
}
public override LcElement Clone()
{
var clone = new QdBerm(Definition as QdBermDef);
clone.Copy(this);
return clone;
}
public override void Copy(LcElement src)
{
base.Copy(src);
var berm = (QdBerm)src;
}
}
13.3.2 BermAction操作类
public class BermAction : DirectComponentAction
{
private PointInputer pointInputer;
private CmdTextInputer cmdTextInputer;
private LcPolyLine OutLoop;
public BermAction() { }
public BermAction(IDocumentEditor docEditor) : base(docEditor)
{
commandCtrl.WriteInfo("命令:Berm");
}
// 多边形绘制出土道路
public async void ExecCreatePoly(string[] args = null)
{
OutLoop = null;
commandCtrl.WriteInfo("绘制出土道路轮廓中...");
var doc = docRt.Document;
cmdTextInputer = new CmdTextInputer(docEditor);
var plAc = new PolyLineAction(docEditor);
await plAc.StartCreating();
if (!await plAc.OtherActionCreating())
goto End;
else
OutLoop = plAc.CurrentPoly;
CreateBerm();
End:
if (OutLoop != null)
vportRt.ActiveElementSet.RemoveElement(OutLoop);
pointInputer = null;
cmdTextInputer = null;
plAc.EndCreating();
EndCreating();
}
// 创建出土道路元素
public void CreateBerm()
{
var doc = docRt.Document;
var bermDef = docRt.GetUseComDef(
$"{NamespaceKey}.道路工程", "出土道路", null) as QdBermDef;
var berm = new QdBerm(bermDef);
berm.Initilize(doc);
var poly = OutLoop.Clone() as LcPolyLine;
berm.Outline = poly.Curve.Clone() as Polyline2d;
berm.ResetBoundingBox();
berm.Layer = GetLayer().Name;
berm.Bottom = 0;
berm.Material = MaterialManager.GetMaterial(MaterialManager.ConcreteUuid);
vportRt.ActiveElementSet.InsertElement(berm);
docRt.Action.ClearSelects();
}
}
13.3.3 QdBermProvider三维模型生成
出土道路的三维Provider负责生成带有坡面的三维模型:
internal static class QdBermProvider
{
internal static void RegistProviders()
{
// 注册二维形状生成器
ConvertToProviders(new List<(string uuid, string name, CreateShape creator)>
{
("UUID-HERE", "出土道路", 出土道路)
});
// 注册三维实体生成器
ConvertToProvider("UUID-HERE", nameof(GetSolid_出土道路),
GetSolid_出土道路, GetSolidMats);
}
// 材质获取
private static MaterialInfo[] GetSolidMats(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator,
Solid3d solid)
{
return new MaterialInfo[] {
pset.GetValue<MaterialInfo>("Material")
};
}
// 二维形状生成
internal static Curve2dGroupCollection 出土道路(
LcParameterSet pset, ShapeCreator creator)
{
var curves = new List<Curve2d>();
var com = creator.ComIns as DirectComponent;
var outline = com.BaseCurve as Polyline2d;
curves = outline.Curve2ds.Clone();
var baseCurveGrp = new Curve2dGroup {
Curve2ds = curves.ToListEx()
};
return new Curve2dGroupCollection { baseCurveGrp };
}
// 三维实体生成
private static Solid3dCollection GetSolid_出土道路(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator)
{
var outline = pset.GetValue<Polyline2d>("Outline");
var bottom = pset.GetValue<double>("Bottom");
var platgeo = CreateBerm(outline);
platgeo.translate(0, 0, bottom);
return new Solid3dCollection()
{
new Solid3d()
{
Name = "Berm",
Geometry = new LightCAD.MathLib.GeometryData()
{
Verteics = platgeo.attributes.position.array,
Indics = platgeo.index.intArray,
Groups = new GeometryGroup[1]
{
new GeometryGroup
{
Name = "Geometry",
Start = 0,
Count = platgeo.index.intArray.Length,
MaterialIndex = 0
},
}
}
}
};
}
// 创建出土道路几何体
private static BufferGeometry CreateBerm(Polyline2d polyline)
{
var shape = new Shape(polyline.GetPoints().ToListEx());
var coodMat = new Matrix4();
coodMat.MakeBasis(
new Vector3(1, 0, 0),
new Vector3(0, 1, 0),
new Vector3(0, 0, 1));
return GeoModelUtil.GetStretchGeometryData(
shape, coodMat, 0, -1).GetBufferGeometry();
}
}
13.4 路面硬化(QdHarden)详解
13.4.1 QdHarden类定义
路面硬化用于施工场地的道路硬化处理:
public class QdHarden : DirectComponent
{
public double Bottom
{
get { return Properties.GetValue<double>("Bottom"); }
set { SetProps((GetPropId(nameof(Bottom)), value)); }
}
public MaterialInfo Material
{
get { return Properties.GetValue<MaterialInfo>("Material"); }
set { SetProps((GetPropId(nameof(Material)), value)); }
}
public Polyline2d Outline
{
get { return this.BaseCurve as Polyline2d; }
set { this.BaseCurve = value; }
}
public QdHarden(QdHardenDef hardenDef) : base(hardenDef)
{
Type = LayoutElementType.Harden;
Outline = new Polyline2d();
}
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds
.SelectMany(n => n.GetPoints()).ToArray());
}
public override LcElement Clone()
{
var clone = new QdHarden(Definition as QdHardenDef);
clone.Copy(this);
return clone;
}
}
13.4.2 HardenAction操作类
HardenAction支持三种创建模式,是道路系统中最灵活的操作类之一:
public class HardenAction : DirectComponentAction
{
private PointInputer pointInputer;
private CmdTextInputer cmdTextInputer;
private LcPolyLine OutLoop;
public HardenAction() { }
public HardenAction(IDocumentEditor docEditor) : base(docEditor)
{
commandCtrl.WriteInfo("命令:Harden");
}
// 多边形绘制路面硬化
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;
CreateHarden();
End:
if (OutLoop != null)
vportRt.ActiveElementSet.RemoveElement(OutLoop);
plAc.EndCreating();
EndCreating();
}
// 矩形绘制路面硬化
public async void ExecCreateRec(string[] args = null)
{
OutLoop = null;
commandCtrl.WriteInfo("绘制路面硬化矩形区域中...");
var plAc = new PolyLineAction(docEditor);
await plAc.StartCreating();
if (!await plAc.OtherActionCreatingRect())
goto End;
else
OutLoop = plAc.CurrentPoly;
CreateHarden();
End:
if (OutLoop != null)
vportRt.ActiveElementSet.RemoveElement(OutLoop);
plAc.EndCreating();
EndCreating();
}
// 从已有线段转换为路面硬化
public async void ExecCreate(string[] args = null)
{
var elementInputer = new ElementSetInputer(this.docEditor);
var result = await elementInputer.Execute("请选择已有闭合线段创建路面硬化:");
if (elementInputer.isCancelled || result == null)
{
this.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;
CreateHarden();
}
}
}
// 创建路面硬化元素
public void CreateHarden()
{
var doc = docRt.Document;
var hardenDef = docRt.GetUseComDef(
$"{NamespaceKey}.道路工程", "路面硬化", null) as QdHardenDef;
var harden = new QdHarden(hardenDef);
harden.Initilize(doc);
var poly = OutLoop.Clone() as LcPolyLine;
harden.Outline = poly.Curve.Clone() as Polyline2d;
harden.ResetBoundingBox();
harden.Layer = GetLayer().Name;
harden.Bottom = 0;
harden.Material = MaterialManager.GetMaterial(
MaterialManager.ConcreteUuid);
vportRt.ActiveElementSet.InsertElement(harden);
docRt.Action.ClearSelects();
}
// 获取或创建图层
private LcLayer GetLayer()
{
var layer = docRt.Document.Layers
.FirstOrDefault(n => n.Name == "Layout_Harden");
if (layer == null)
{
layer = docRt.Document.CreateObject<LcLayer>();
layer.Name = "Layout_Harden";
layer.Color = 0x808080; // 灰色
layer.SetLineType(new LcLineType("ByLayer"));
layer.Transparency = 0;
docRt.Document.Layers.Add(layer);
}
return layer;
}
}
13.4.3 QdHardenProvider三维生成
internal static class QdHardenProvider
{
internal static void RegistProviders()
{
ConvertToProviders(new List<(string uuid, string name, CreateShape creator)>
{
("UUID", "路面硬化", 路面硬化)
});
ConvertToProvider("UUID", nameof(GetSolid_路面硬化),
GetSolid_路面硬化, GetSolidMats);
}
private static MaterialInfo[] GetSolidMats(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator,
Solid3d solid)
{
return new MaterialInfo[] {
pset.GetValue<MaterialInfo>("Material")
};
}
internal static Curve2dGroupCollection 路面硬化(
LcParameterSet pset, ShapeCreator creator)
{
var com = creator.ComIns as DirectComponent;
var outline = com.BaseCurve as Polyline2d;
var curves = outline.Curve2ds.Clone();
var baseCurveGrp = new Curve2dGroup {
Curve2ds = curves.ToListEx()
};
return new Curve2dGroupCollection { baseCurveGrp };
}
private static Solid3dCollection GetSolid_路面硬化(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator)
{
var outline = pset.GetValue<Polyline2d>("Outline");
var bottom = pset.GetValue<double>("Bottom");
var shape = new Shape(outline.GetPoints().ToListEx());
var coodMat = new Matrix4();
coodMat.MakeBasis(
new Vector3(1, 0, 0),
new Vector3(0, 1, 0),
new Vector3(0, 0, 1));
var platgeo = GeoModelUtil.GetStretchGeometryData(
shape, coodMat, 0, -1).GetBufferGeometry();
platgeo.translate(0, 0, bottom);
return new Solid3dCollection()
{
new Solid3d()
{
Name = "Harden",
Geometry = new GeometryData()
{
Verteics = platgeo.attributes.position.array,
Indics = platgeo.index.intArray,
Groups = new GeometryGroup[1]
{
new GeometryGroup
{
Name = "Geometry",
Start = 0,
Count = platgeo.index.intArray.Length,
MaterialIndex = 0
}
}
}
}
};
}
}
13.5 硬化地面(QdGround)详解
13.5.1 QdGround类定义
硬化地面与路面硬化类似,但侧重于场地整体的硬化区域设计:
public class QdGround : DirectComponent
{
public double Bottom
{
get { return Properties.GetValue<double>("Bottom"); }
set { SetProps((GetPropId(nameof(Bottom)), value)); }
}
public MaterialInfo Material
{
get { return Properties.GetValue<MaterialInfo>("Material"); }
set { SetProps((GetPropId(nameof(Material)), value)); }
}
public Polyline2d Outline
{
get { return this.BaseCurve as Polyline2d; }
set { this.BaseCurve = value; }
}
public QdGround(QdGroundDef groundDef) : base(groundDef)
{
Type = LayoutElementType.Ground;
Outline = new Polyline2d();
}
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds
.SelectMany(n => n.GetPoints()).ToArray());
}
public override LcElement Clone()
{
var clone = new QdGround(Definition as QdGroundDef);
clone.Copy(this);
return clone;
}
}
13.5.2 GroundAction操作类
GroundAction同样支持三种创建模式(多边形、矩形、转换),其命令注册如下:
// LayoutCmds.cs 中的命令注册
[CommandMethod(Name = "Ground", ShortCuts = "GOD")]
public CommandResult DrawGround(IDocumentEditor docEditor, string[] args)
{
var groundAction = new GroundAction(docEditor);
groundAction.ExecCreatePoly(args);
return CommandResult.Succ();
}
[CommandMethod(Name = "GroundRec", ShortCuts = "GDRC")]
public CommandResult DrawGroundRec(IDocumentEditor docEditor, string[] args)
{
var groundAction = new GroundAction(docEditor);
groundAction.ExecCreateRec(args);
return CommandResult.Succ();
}
[CommandMethod(Name = "GroundChange", ShortCuts = "GODCH")]
public CommandResult DrawGroundChange(IDocumentEditor docEditor, string[] args)
{
var groundAction = new GroundAction(docEditor);
groundAction.ExecCreate(args);
return CommandResult.Succ();
}
13.5.3 QdGroundProvider
QdGroundProvider在QdLayoutProviderRegist.cs中被注册:
public (ShapeProviderCollection, SolidProviderCollection) GetImportProviders()
{
// ... 其他Provider注册 ...
QdGroundProvider.RegistProviders();
// ...
return (ShapeProviders, SolidProviders);
}
13.6 交叉口处理系统
13.6.1 QdIntersectionProvider概述
道路交叉口是道路系统中最具挑战性的部分。QdIntersectionProvider负责处理多条道路交汇时的几何计算和三维模型生成。
internal static class QdIntersectionProvider
{
internal static void RegistProviders()
{
// 注册交叉口的形状和实体生成器
ConvertToProviders(new List<(string uuid, string name, CreateShape creator)>
{
("UUID", "交叉口", 交叉口)
});
ConvertToProvider("UUID", nameof(GetSolid_交叉口),
GetSolid_交叉口, GetSolidMats);
}
}
13.6.2 交叉口几何计算原理
交叉口处理的核心算法步骤:
- 检测交叉:判断哪些道路在几何上相交
- 计算交点:使用
Intersect2d工具类计算精确交点 - 裁剪边界:在交叉区域裁剪各条道路的边界线
- 生成过渡:在交叉口区域生成平滑的过渡面
- 材质分配:为交叉口区域分配适当的材质
// 交叉口的核心几何计算
// 使用Intersect2d提供的各种相交计算方法:
// - XLineWithXLine: 延长线与延长线的交点
// - CircleWithXLine: 圆与延长线的交点
// - CircleWithCircle: 圆与圆的交点
// - IsPolygonWithLine: 多边形与线段的相交判断
// - IsPolygonWithArc: 多边形与圆弧的相交判断
13.6.3 交叉口三维模型
交叉口的三维模型生成需要考虑:
- 各路段的高程差异
- 交叉口区域的平滑过渡
- 转弯半径的圆角处理
- 路面标线和标识
13.7 曲线闭合工具(LcCurveChangeLoop)
13.7.1 LcCurveChangeLoop的作用
在道路系统中,LcCurveChangeLoop类负责将用户选择的多条线段自动检测并组合为闭合环路。这是”转换”创建模式(如HardenChange、GroundChange)的核心工具。
public class LcCurveChangeLoop
{
public static List<LcPolyLine> CheckLoops(List<LcCurve2d> eles)
{
var polys = new List<LcPolyLine>();
for (var i = 0; i < eles.Count; i++)
{
var poly = new Polyline2d();
poly.Curve2ds = new List<Curve2d>();
var line = eles[i];
ChangeLineToPolyLine(poly, line);
// 处理圆形
if (eles[i] is LcCircle)
{
poly.IsClosed = true;
polys.Add(new LcPolyLine() {
Curve2ds = poly.Curve2ds.ToLcListCurve(),
Curve = poly
});
continue;
}
// 检查是否已闭合
if (poly.Curve2ds.First().GetPoints(1).First()
.Similarity(poly.Curve2ds.Last().GetPoints(1).Last(), 0))
{
poly.IsClosed = true;
polys.Add(new LcPolyLine() {
Curve2ds = poly.Curve2ds.ToLcListCurve(),
Curve = poly
});
continue;
}
// 尝试从剩余线段中找到可以连接的线段
var flag = false;
for (var k = eles.Count - 1; k > i; k--)
{
for (var j = eles.Count - 1; j > i; j--)
{
var ele = eles[j];
if (ChangeLineToPolyLine(poly, ele))
{
eles.Remove(ele);
break;
}
}
if (poly.Curve2ds.Count > 1 &&
poly.Curve2ds.First().GetPoints(1).First()
.Similarity(poly.Curve2ds.Last().GetPoints(1).Last(), 0))
{
flag = true;
break;
}
}
if (flag)
{
poly.IsClosed = true;
polys.Add(new LcPolyLine() {
Curve2ds = poly.Curve2ds.ToLcListCurve(),
Curve = poly
});
}
}
// 初始化所有闭合环路
foreach (var poly in polys)
{
poly.Initilize(eles.First().Document);
}
return polys;
}
}
13.7.2 线段拼接算法
ChangeLineToPolyLine方法负责将各种类型的线段拼接到多段线中:
private static bool ChangeLineToPolyLine(Polyline2d lcPoly, LcCurve2d element)
{
if (element.Type == BuiltinElementType.Line)
{
var line = element as LcLine;
if (lcPoly.Curve2ds.Count == 0)
{
// 第一条线段直接添加
lcPoly.Curve2ds.Add(new Line2d(line.Start.Clone(), line.End.Clone()));
return true;
}
else
{
var lastP = lcPoly.Curve2ds.Last().GetPoints(1).Last();
if (lastP.Similarity(line.Start, 0))
{
// 终点连接起点,正向添加
lcPoly.Curve2ds.Add(line.Curve);
return true;
}
else if (lastP.Similarity(line.End, 0))
{
// 终点连接终点,反向添加
line.Curve.Reverse();
lcPoly.Curve2ds.Add(line.Curve);
return true;
}
}
}
else if (element.Type == BuiltinElementType.PloyLine)
{
// 多段线拼接逻辑
var poly = element as LcPolyLine;
// ... 类似的起点终点匹配逻辑
}
else if (element.Type == BuiltinElementType.Arc)
{
// 圆弧拼接逻辑
var arc = element as LcArc;
// ... 类似的起点终点匹配逻辑
}
else if (element.Type == BuiltinElementType.Circle)
{
// 圆形特殊处理:拆分为两段半圆弧
var circle = element as LcCircle;
if (lcPoly.Curve2ds.Count == 0)
{
var arc1 = new Arc2d();
arc1.StartAngle = 0;
arc1.EndAngle = Math.PI;
arc1.Center = circle.Center.Clone();
arc1.Radius = circle.Radius;
// ... 设置端点
var arc2 = new Arc2d();
arc2.StartAngle = Math.PI;
arc2.EndAngle = Math.PI * 2;
// ... 设置端点
lcPoly.Curve2ds.Add(arc1);
lcPoly.Curve2ds.Add(arc2);
return true;
}
}
return false;
}
13.8 道路图层管理
13.8.1 图层命名规范
FY_Layout为每种道路类型定义了专用的图层:
| 道路类型 | 图层名称 | 颜色 | 说明 |
|---|---|---|---|
| 城市道路 | Layout_Road | 灰色(0x808080) | 主要交通道路 |
| 出土道路 | Layout_Berm | 棕色(0x8B4513) | 土方运输道路 |
| 路面硬化 | Layout_Harden | 深灰(0x696969) | 硬化路面区域 |
| 硬化地面 | Layout_Ground | 浅灰(0xA9A9A9) | 场地硬化区域 |
13.8.2 图层创建模式
所有道路Action类都遵循相同的图层创建模式:
private LcLayer GetLayer()
{
// 查找已有图层
var layer = docRt.Document.Layers
.FirstOrDefault(n => n.Name == "Layout_Road");
if (layer == null)
{
// 图层不存在时自动创建
layer = docRt.Document.CreateObject<LcLayer>();
layer.Name = "Layout_Road";
layer.Color = 0x808080;
layer.SetLineType(new LcLineType("ByLayer"));
layer.Transparency = 0;
docRt.Document.Layers.Add(layer);
}
return layer;
}
13.9 道路系统完整命令一览
13.9.1 命令汇总表
| 命令名 | 快捷键 | 功能说明 | Action类 | 创建方法 |
|---|---|---|---|---|
| Road | ROD | 城市道路绘制 | RoadAction | ExecCreate |
| Berm | BRM | 出土道路绘制 | BermAction | ExecCreatePoly |
| Harden | HDR | 路面硬化(多边形) | HardenAction | ExecCreatePoly |
| HardenRec | HDRC | 路面硬化(矩形) | HardenAction | ExecCreateRec |
| HardenChange | HDCH | 路面硬化(转换) | HardenAction | ExecCreate |
| Ground | GOD | 硬化地面(多边形) | GroundAction | ExecCreatePoly |
| GroundRec | GDRC | 硬化地面(矩形) | GroundAction | ExecCreateRec |
| GroundChange | GODCH | 硬化地面(转换) | GroundAction | ExecCreate |
13.9.2 命令注册源码
完整的道路命令注册代码(摘自LayoutCmds.cs):
[CommandClass]
public class LayoutCmds
{
// 城市道路
[CommandMethod(Name = "Road", ShortCuts = "ROD")]
public CommandResult DrawRoad(IDocumentEditor docEditor, string[] args)
{
var roadAction = new RoadAction(docEditor);
roadAction.ExecCreate(args);
return CommandResult.Succ();
}
// 出土道路
[CommandMethod(Name = "Berm", ShortCuts = "BRM")]
public CommandResult DrawBerm(IDocumentEditor docEditor, string[] args)
{
var bermAction = new BermAction(docEditor);
bermAction.ExecCreatePoly(args);
return CommandResult.Succ();
}
// 路面硬化
[CommandMethod(Name = "Harden", ShortCuts = "HDR")]
public CommandResult DrawHarden(IDocumentEditor docEditor, string[] args)
{
var hardenAction = new HardenAction(docEditor);
hardenAction.ExecCreatePoly(args);
return CommandResult.Succ();
}
[CommandMethod(Name = "HardenRec", ShortCuts = "HDRC")]
public CommandResult DrawHardenRec(IDocumentEditor docEditor, string[] args)
{
var hardenAction = new HardenAction(docEditor);
hardenAction.ExecCreateRec(args);
return CommandResult.Succ();
}
[CommandMethod(Name = "HardenChange", ShortCuts = "HDCH")]
public CommandResult DrawHardenChange(IDocumentEditor docEditor, string[] args)
{
var hardenAction = new HardenAction(docEditor);
hardenAction.ExecCreate(args);
return CommandResult.Succ();
}
// 硬化地面
[CommandMethod(Name = "Ground", ShortCuts = "GOD")]
public CommandResult DrawGround(IDocumentEditor docEditor, string[] args)
{
var groundAction = new GroundAction(docEditor);
groundAction.ExecCreatePoly(args);
return CommandResult.Succ();
}
[CommandMethod(Name = "GroundRec", ShortCuts = "GDRC")]
public CommandResult DrawGroundRec(IDocumentEditor docEditor, string[] args)
{
var groundAction = new GroundAction(docEditor);
groundAction.ExecCreateRec(args);
return CommandResult.Succ();
}
[CommandMethod(Name = "GroundChange", ShortCuts = "GODCH")]
public CommandResult DrawGroundChange(IDocumentEditor docEditor, string[] args)
{
var groundAction = new GroundAction(docEditor);
groundAction.ExecCreate(args);
return CommandResult.Succ();
}
}
13.10 扩展开发:自定义道路类型
13.10.1 开发步骤概述
创建自定义道路类型需要以下步骤:
- 定义元素类(继承DirectComponent)
- 定义元素定义类(继承LcComponentDefinition)
- 实现操作类(继承DirectComponentAction)
- 实现三维操作类(实现IElement3dAction)
- 创建Provider(Shape和Solid)
- 注册元素类型和命令
13.10.2 示例:创建人行道元素
// 1. 定义元素类型
public static ElementType Sidewalk = new ElementType
{
Guid = Guid.ParseExact("{YOUR-GUID-HERE}", "B").ToLcGuid(),
Name = "Sidewalk",
DispalyName = "人行道",
ClassType = typeof(QdSidewalk)
};
// 2. 定义元素类
public class QdSidewalk : DirectComponent
{
public double Bottom
{
get { return Properties.GetValue<double>("Bottom"); }
set { SetProps((GetPropId(nameof(Bottom)), value)); }
}
public double Width
{
get { return Properties.GetValue<double>("Width"); }
set { SetProps((GetPropId(nameof(Width)), value)); }
}
public MaterialInfo Material
{
get { return Properties.GetValue<MaterialInfo>("Material"); }
set { SetProps((GetPropId(nameof(Material)), value)); }
}
public Polyline2d Outline
{
get { return this.BaseCurve as Polyline2d; }
set { this.BaseCurve = value; }
}
public QdSidewalk(QdSidewalkDef def) : base(def)
{
Type = CustomElementType.Sidewalk;
Outline = new Polyline2d();
}
public override Box2 GetBoundingBox()
{
return new Box2().ExpandByPoints(
GetShapes()[0].Curve2ds
.SelectMany(n => n.GetPoints()).ToArray());
}
public override LcElement Clone()
{
var clone = new QdSidewalk(Definition as QdSidewalkDef);
clone.Copy(this);
return clone;
}
}
// 3. 实现操作类
public class SidewalkAction : DirectComponentAction
{
private LcPolyLine OutLoop;
public SidewalkAction() { }
public SidewalkAction(IDocumentEditor docEditor) : base(docEditor)
{
commandCtrl.WriteInfo("命令:Sidewalk");
}
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;
CreateSidewalk();
End:
if (OutLoop != null)
vportRt.ActiveElementSet.RemoveElement(OutLoop);
plAc.EndCreating();
EndCreating();
}
public void CreateSidewalk()
{
var doc = docRt.Document;
var def = docRt.GetUseComDef(
$"{NamespaceKey}.道路工程", "人行道", null) as QdSidewalkDef;
var sidewalk = new QdSidewalk(def);
sidewalk.Initilize(doc);
var poly = OutLoop.Clone() as LcPolyLine;
sidewalk.Outline = poly.Curve.Clone() as Polyline2d;
sidewalk.ResetBoundingBox();
sidewalk.Layer = GetLayer().Name;
sidewalk.Bottom = 0;
sidewalk.Width = 2000; // 默认2m宽
sidewalk.Material = MaterialManager.GetMaterial(
MaterialManager.ConcreteUuid);
vportRt.ActiveElementSet.InsertElement(sidewalk);
docRt.Action.ClearSelects();
}
private LcLayer GetLayer()
{
var layer = docRt.Document.Layers
.FirstOrDefault(n => n.Name == "Layout_Sidewalk");
if (layer == null)
{
layer = docRt.Document.CreateObject<LcLayer>();
layer.Name = "Layout_Sidewalk";
layer.Color = 0xC0C0C0;
layer.SetLineType(new LcLineType("ByLayer"));
layer.Transparency = 0;
docRt.Document.Layers.Add(layer);
}
return layer;
}
}
// 4. 注册命令
[CommandMethod(Name = "Sidewalk", ShortCuts = "SW")]
public CommandResult DrawSidewalk(IDocumentEditor docEditor, string[] args)
{
var action = new SidewalkAction(docEditor);
action.ExecCreatePoly(args);
return CommandResult.Succ();
}
13.10.3 注册到插件系统
在LayoutPlugin.cs的Loaded方法中注册:
public void Loaded()
{
// 注册元素类型
LcDocument.RegistElementTypes(new ElementType[] {
CustomElementType.Sidewalk
});
// 注册2D操作
LcDocument.ElementActions.Add(
CustomElementType.Sidewalk, new SidewalkAction());
// 注册3D操作
LcDocument.Element3dActions.Add(
CustomElementType.Sidewalk, new Sidewalk3dAction());
}
13.11 本章小结
本章详细介绍了FY_Layout道路与交叉口系统的设计与实现:
- 四种道路元素:Road(城市道路)、Berm(出土道路)、Harden(路面硬化)、Ground(硬化地面),各有不同的应用场景和设计特点
- 统一的开发模式:所有道路元素都遵循Element → Action → Provider的三层架构
- 多种创建方式:多边形绘制、矩形绘制、已有线段转换,满足不同设计场景的需求
- 交叉口处理:QdIntersectionProvider专门处理道路交叉口的复杂几何计算
- 曲线闭合工具:LcCurveChangeLoop实现了智能的线段自动拼接和闭合检测
- 图层管理:每种道路类型都有专用图层,支持分层管理和显示控制
- 扩展性:通过遵循现有的开发模式,可以方便地创建自定义道路类型
道路系统是场布设计中最常用的功能之一,掌握其设计模式和实现细节,对于理解和扩展FY_Layout具有重要意义。
| 上一章:二次开发实战案例 | 下一章:设备与模板排布系统 |