第十五章:文件格式与数据交换
15.1 概述
15.1.1 FY_Layout的文件格式支持
飞扬集成设计平台(FY_Layout)作为一个开放的BIM设计平台,在文件格式和数据交换方面具有良好的兼容性。基于LightCAD框架,平台支持多种主流CAD/BIM文件格式的读写操作。
核心文件格式支持包括:
| 格式 | 扩展名 | 读取 | 写入 | 依赖库 |
|---|---|---|---|---|
| DWG | .dwg | ✅ | ✅ | netDxf |
| DXF | .dxf | ✅ | ✅ | netDxf |
| SVG | .svg | ✅ | ✅ | Svg |
| JSON | .json | ✅ | ✅ | Newtonsoft.Json |
| SQLite | .db | ✅ | ✅ | Dapper + SQLite |
15.1.2 依赖库说明
FY_Layout的文件格式支持依赖以下第三方库(在QdLayout.csproj中引用):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<!-- DXF/DWG格式支持 -->
<Reference Include="netDxf">
<HintPath>..\Libs\netDxf.dll</HintPath>
</Reference>
<!-- JSON序列化 -->
<Reference Include="Newtonsoft.Json">
<HintPath>..\Libs\Newtonsoft.Json.dll</HintPath>
</Reference>
<!-- SVG格式支持 -->
<Reference Include="Svg">
<HintPath>..\Libs\Svg.dll</HintPath>
</Reference>
<!-- 数据库访问 -->
<Reference Include="Dapper">
<HintPath>..\Libs\Dapper.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
15.2 DWG/DXF格式支持
15.2.1 netDxf库简介
FY_Layout使用netDxf开源库实现DWG和DXF格式的读写。netDxf是一个纯C#实现的DXF格式解析库,支持AutoCAD DXF格式从R12到最新版本。
15.2.2 DXF文件读取
// DXF文件读取示例
using netDxf;
public class DxfImporter
{
public static void ImportDxfFile(string filePath, LcDocument document)
{
// 打开DXF文件
var dxfDocument = DxfDocument.Load(filePath);
if (dxfDocument == null)
{
throw new Exception($"无法打开DXF文件: {filePath}");
}
// 导入图层
foreach (var layer in dxfDocument.Layers)
{
var lcLayer = document.CreateObject<LcLayer>();
lcLayer.Name = layer.Name;
lcLayer.Color = ConvertColor(layer.Color);
document.Layers.Add(lcLayer);
}
// 导入线段
foreach (var line in dxfDocument.Entities.Lines)
{
var lcLine = document.CreateObject<LcLine>();
lcLine.Start = new Vector2(line.StartPoint.X, line.StartPoint.Y);
lcLine.End = new Vector2(line.EndPoint.X, line.EndPoint.Y);
lcLine.Layer = line.Layer.Name;
document.Elements.Add(lcLine);
}
// 导入多段线
foreach (var polyline in dxfDocument.Entities.Polylines2D)
{
var lcPoly = document.CreateObject<LcPolyLine>();
var poly2d = new Polyline2d();
var vertices = polyline.Vertexes.ToList();
for (int i = 0; i < vertices.Count - 1; i++)
{
poly2d.Curve2ds.Add(new Line2d(
new Vector2(vertices[i].Position.X, vertices[i].Position.Y),
new Vector2(vertices[i+1].Position.X, vertices[i+1].Position.Y)
));
}
if (polyline.IsClosed && vertices.Count > 2)
{
poly2d.Curve2ds.Add(new Line2d(
new Vector2(vertices.Last().Position.X, vertices.Last().Position.Y),
new Vector2(vertices.First().Position.X, vertices.First().Position.Y)
));
poly2d.IsClosed = true;
}
lcPoly.Curve = poly2d;
lcPoly.Layer = polyline.Layer.Name;
document.Elements.Add(lcPoly);
}
// 导入圆弧
foreach (var arc in dxfDocument.Entities.Arcs)
{
var lcArc = document.CreateObject<LcArc>();
lcArc.Curve = new Arc2d
{
Center = new Vector2(arc.Center.X, arc.Center.Y),
Radius = arc.Radius,
StartAngle = arc.StartAngle * Math.PI / 180,
EndAngle = arc.EndAngle * Math.PI / 180
};
lcArc.Layer = arc.Layer.Name;
document.Elements.Add(lcArc);
}
// 导入圆
foreach (var circle in dxfDocument.Entities.Circles)
{
var lcCircle = document.CreateObject<LcCircle>();
lcCircle.Center = new Vector2(circle.Center.X, circle.Center.Y);
lcCircle.Radius = circle.Radius;
lcCircle.Layer = circle.Layer.Name;
document.Elements.Add(lcCircle);
}
}
private static int ConvertColor(AciColor color)
{
// ACI颜色转换为RGB整数
var rgb = color.ToColor();
return (rgb.R << 16) | (rgb.G << 8) | rgb.B;
}
}
15.2.3 DXF文件导出
// DXF文件导出示例
public class DxfExporter
{
public static void ExportToDxf(LcDocument document, string filePath)
{
var dxfDoc = new DxfDocument();
// 导出图层
foreach (var layer in document.Layers)
{
var dxfLayer = new netDxf.Tables.Layer(layer.Name)
{
Color = new AciColor(
(byte)((layer.Color >> 16) & 0xFF),
(byte)((layer.Color >> 8) & 0xFF),
(byte)(layer.Color & 0xFF))
};
dxfDoc.Layers.Add(dxfLayer);
}
// 导出场布元素
foreach (var element in document.Elements)
{
if (element is QdLawn lawn)
{
ExportLawn(dxfDoc, lawn);
}
else if (element is QdFoundationPit pit)
{
ExportFoundationPit(dxfDoc, pit);
}
else if (element is QdFence fence)
{
ExportFence(dxfDoc, fence);
}
// ... 其他元素类型
}
dxfDoc.Save(filePath);
}
private static void ExportLawn(DxfDocument dxfDoc, QdLawn lawn)
{
// 将草坪轮廓导出为DXF多段线
var vertices = new List<netDxf.Entities.Polyline2DVertex>();
foreach (var curve in lawn.Outline.Curve2ds)
{
if (curve is Line2d line)
{
vertices.Add(new netDxf.Entities.Polyline2DVertex(
line.Start.X, line.Start.Y));
}
}
var polyline = new netDxf.Entities.Polyline2D(vertices, true);
polyline.Layer = new netDxf.Tables.Layer("Layout_Lawn");
dxfDoc.Entities.Add(polyline);
}
}
15.3 JSON数据序列化
15.3.1 元素序列化机制
FY_Layout使用Newtonsoft.Json进行数据的序列化和反序列化,特别是在元素属性的持久化和配置管理中。
// 元素属性序列化
public class ElementSerializer
{
// 序列化场布元素到JSON
public static string SerializeElement(DirectComponent element)
{
var data = new Dictionary<string, object>
{
["Type"] = element.Type.Name,
["Guid"] = element.Type.Guid.ToString(),
["Layer"] = element.Layer,
["Properties"] = SerializeProperties(element.Properties)
};
// 序列化几何数据
if (element.BaseCurve is Polyline2d outline)
{
data["Outline"] = SerializePolyline(outline);
}
return JsonConvert.SerializeObject(data, Formatting.Indented);
}
// 序列化属性集
private static Dictionary<string, object> SerializeProperties(
LcParameterSet properties)
{
var dict = new Dictionary<string, object>();
foreach (var key in properties.Keys)
{
var value = properties[key];
if (value is MaterialInfo material)
{
dict[key] = new
{
Uuid = material.Uuid,
Name = material.Name
};
}
else
{
dict[key] = value;
}
}
return dict;
}
// 序列化多段线
private static List<double[]> SerializePolyline(Polyline2d polyline)
{
var points = new List<double[]>();
foreach (var curve in polyline.Curve2ds)
{
foreach (var point in curve.GetPoints())
{
points.Add(new[] { point.X, point.Y });
}
}
return points;
}
}
15.3.2 反序列化元素
// 从JSON反序列化场布元素
public static DirectComponent DeserializeElement(
string json, LcDocument document, DocumentRuntime docRt)
{
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
var typeName = data["Type"].ToString();
DirectComponent element = null;
switch (typeName)
{
case "Lawn":
var lawnDef = docRt.GetUseComDef(
"绿色文明", "草坪", null) as QdLawnDef;
var lawn = new QdLawn(lawnDef);
lawn.Initilize(document);
element = lawn;
break;
case "FoundationPit":
var pitDef = docRt.GetUseComDef(
"基坑工程", "基坑", null) as QdFoundationPitDef;
var pit = new QdFoundationPit(pitDef);
pit.Initilize(document);
element = pit;
break;
// ... 其他元素类型
}
if (element != null)
{
// 恢复属性
var properties = JsonConvert.DeserializeObject<Dictionary<string, object>>(
data["Properties"].ToString());
foreach (var kvp in properties)
{
element.Properties.SetValue(kvp.Key, kvp.Value);
}
// 恢复几何数据
if (data.ContainsKey("Outline"))
{
var points = JsonConvert.DeserializeObject<List<double[]>>(
data["Outline"].ToString());
var outline = new Polyline2d();
for (int i = 0; i < points.Count - 1; i++)
{
outline.Curve2ds.Add(new Line2d(
new Vector2(points[i][0], points[i][1]),
new Vector2(points[i+1][0], points[i+1][1])));
}
element.BaseCurve = outline;
}
element.Layer = data["Layer"].ToString();
element.ResetBoundingBox();
}
return element;
}
15.3.3 配置文件管理
FY_Layout使用JSON格式存储场布配置信息:
// 场布配置类 - QdLayoutCategorySettings
public class QdLayoutCategorySettings
{
// 默认图层颜色配置
public Dictionary<string, int> LayerColors { get; set; }
= new Dictionary<string, int>
{
["Layout_Lawn"] = 0x00FF00, // 绿色
["Layout_Fence"] = 0x808080, // 灰色
["Layout_FoundationPit"] = 0xFFFF00, // 黄色
["Layout_Road"] = 0x808080, // 灰色
["Layout_Berm"] = 0x8B4513, // 棕色
["Layout_Barrier"] = 0xFF0000, // 红色
};
// 默认材质配置
public Dictionary<string, string> DefaultMaterials { get; set; }
= new Dictionary<string, string>
{
["Lawn"] = "Lawn",
["Fence"] = "Concrete",
["FoundationPit"] = "Metal1",
["Road"] = "Concrete",
};
// 保存配置
public void Save(string filePath)
{
var json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(filePath, json);
}
// 加载配置
public static QdLayoutCategorySettings Load(string filePath)
{
if (!File.Exists(filePath))
return new QdLayoutCategorySettings();
var json = File.ReadAllText(filePath);
return JsonConvert.DeserializeObject<QdLayoutCategorySettings>(json);
}
}
15.4 SQLite数据存储
15.4.1 Dapper ORM集成
FY_Layout通过Dapper和SQLite实现结构化数据的持久化存储:
// 使用Dapper进行数据库操作
using Dapper;
using System.Data.SQLite;
public class LayoutDataStore
{
private readonly string connectionString;
public LayoutDataStore(string dbPath)
{
connectionString = $"Data Source={dbPath};Version=3;";
InitializeDatabase();
}
// 初始化数据库表结构
private void InitializeDatabase()
{
using var conn = new SQLiteConnection(connectionString);
conn.Open();
conn.Execute(@"
CREATE TABLE IF NOT EXISTS LayoutElements (
Id TEXT PRIMARY KEY,
TypeName TEXT NOT NULL,
LayerName TEXT,
Properties TEXT,
GeometryData TEXT,
CreateTime DATETIME DEFAULT CURRENT_TIMESTAMP,
UpdateTime DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS PlateRoomConfig (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
RoomName TEXT NOT NULL,
Width REAL NOT NULL,
Height REAL NOT NULL,
Depth REAL NOT NULL,
Description TEXT
);
");
}
// 保存场布元素
public void SaveElement(DirectComponent element)
{
using var conn = new SQLiteConnection(connectionString);
conn.Open();
var json = ElementSerializer.SerializeElement(element);
conn.Execute(@"
INSERT OR REPLACE INTO LayoutElements
(Id, TypeName, LayerName, Properties, GeometryData, UpdateTime)
VALUES (@Id, @TypeName, @LayerName, @Properties, @GeometryData,
CURRENT_TIMESTAMP)",
new
{
Id = element.Id.ToString(),
TypeName = element.Type.Name,
LayerName = element.Layer,
Properties = json,
GeometryData = ""
});
}
// 加载所有场布元素
public List<dynamic> LoadElements()
{
using var conn = new SQLiteConnection(connectionString);
conn.Open();
return conn.Query("SELECT * FROM LayoutElements").ToList();
}
}
15.4.2 板房配置数据库
板房系统使用SQLite存储房间配置信息:
// 板房房间配置管理
public class PlateRoomConfigManager
{
private readonly LayoutDataStore dataStore;
public PlateRoomConfigManager(LayoutDataStore store)
{
dataStore = store;
}
// 获取预定义的房间配置
public List<PlateRoomConfig> GetRoomConfigs()
{
using var conn = new SQLiteConnection(dataStore.ConnectionString);
conn.Open();
return conn.Query<PlateRoomConfig>(
"SELECT * FROM PlateRoomConfig ORDER BY RoomName").ToList();
}
// 保存房间配置
public void SaveRoomConfig(PlateRoomConfig config)
{
using var conn = new SQLiteConnection(dataStore.ConnectionString);
conn.Open();
conn.Execute(@"
INSERT INTO PlateRoomConfig
(RoomName, Width, Height, Depth, Description)
VALUES (@RoomName, @Width, @Height, @Depth, @Description)",
config);
}
}
// 房间配置数据类
public class PlateRoomConfig
{
public int Id { get; set; }
public string RoomName { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double Depth { get; set; }
public string Description { get; set; }
}
15.5 SVG格式导出
15.5.1 二维图形SVG导出
FY_Layout支持将二维设计图导出为SVG格式,便于在网页中展示:
// SVG导出工具
using Svg;
public class SvgExporter
{
public static void ExportToSvg(LcDocument document, string filePath)
{
// 计算文档范围
var bounds = CalculateBounds(document);
var svgDoc = new SvgDocument
{
Width = new SvgUnit(SvgUnitType.Pixel, (float)bounds.Width),
Height = new SvgUnit(SvgUnitType.Pixel, (float)bounds.Height),
ViewBox = new SvgViewBox(
(float)bounds.Min.X, (float)bounds.Min.Y,
(float)bounds.Width, (float)bounds.Height)
};
// 导出各图层元素
foreach (var element in document.Elements)
{
if (element is QdLawn lawn)
{
ExportLawnToSvg(svgDoc, lawn);
}
else if (element is QdFence fence)
{
ExportFenceToSvg(svgDoc, fence);
}
// ... 其他元素
}
svgDoc.Write(filePath);
}
private static void ExportLawnToSvg(SvgDocument svgDoc, QdLawn lawn)
{
var polygon = new SvgPolygon();
var points = new SvgPointCollection();
foreach (var curve in lawn.Outline.Curve2ds)
{
if (curve is Line2d line)
{
points.Add(new SvgUnit(SvgUnitType.Pixel, (float)line.Start.X));
points.Add(new SvgUnit(SvgUnitType.Pixel, (float)line.Start.Y));
}
}
polygon.Points = points;
polygon.Fill = new SvgColourServer(System.Drawing.Color.Green);
polygon.FillOpacity = 0.3f;
polygon.Stroke = new SvgColourServer(System.Drawing.Color.DarkGreen);
polygon.StrokeWidth = new SvgUnit(1);
svgDoc.Children.Add(polygon);
}
}
15.6 三维模型数据格式
15.6.1 GeometryData结构
FY_Layout的三维模型使用自定义的GeometryData结构存储几何数据:
// LightCAD.MathLib.GeometryData 结构
public class GeometryData
{
// 顶点数组(x,y,z交替存储)
public double[] Verteics { get; set; }
// 索引数组(三角面片索引)
public int[] Indics { get; set; }
// 几何分组(用于多材质分配)
public GeometryGroup[] Groups { get; set; }
}
public class GeometryGroup
{
public string Name { get; set; }
public int Start { get; set; } // 起始索引
public int Count { get; set; } // 索引数量
public int MaterialIndex { get; set; } // 材质索引
}
15.6.2 Solid3d与三维导出
// 三维实体数据结构
public class Solid3d
{
public string Name { get; set; }
public GeometryData Geometry { get; set; }
}
public class Solid3dCollection : List<Solid3d> { }
// Provider生成三维数据示例(草坪)
private static Solid3dCollection GetSolid_草坪(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator)
{
var outline = pset.GetValue<Polyline2d>("Outline");
var bottom = pset.GetValue<double>("Bottom");
// 使用ThreeJs4Net的Shape创建几何体
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 = "Lawn",
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
}
}
}
}
};
}
15.6.3 三维数据导出到通用格式
// 将三维数据导出为OBJ格式
public class ObjExporter
{
public static void ExportToObj(
Solid3dCollection solids, string filePath)
{
using var writer = new StreamWriter(filePath);
writer.WriteLine("# FY_Layout 3D Export");
writer.WriteLine($"# Generated: {DateTime.Now}");
int vertexOffset = 0;
foreach (var solid in solids)
{
writer.WriteLine($"o {solid.Name}");
var vertices = solid.Geometry.Verteics;
var indices = solid.Geometry.Indics;
// 写入顶点
for (int i = 0; i < vertices.Length; i += 3)
{
writer.WriteLine($"v {vertices[i]:F6} " +
$"{vertices[i+1]:F6} {vertices[i+2]:F6}");
}
// 写入面片(OBJ索引从1开始)
for (int i = 0; i < indices.Length; i += 3)
{
writer.WriteLine($"f " +
$"{indices[i] + 1 + vertexOffset} " +
$"{indices[i+1] + 1 + vertexOffset} " +
$"{indices[i+2] + 1 + vertexOffset}");
}
vertexOffset += vertices.Length / 3;
}
}
}
15.7 材质系统与数据交换
15.7.1 MaterialInfo结构
// 材质信息类
public class MaterialInfo
{
public string Uuid { get; set; } // 材质唯一标识
public string Name { get; set; } // 材质名称
public int Color { get; set; } // 颜色值
public float Opacity { get; set; } // 不透明度
public string TexturePath { get; set; } // 贴图路径
}
// 材质管理器 - 提供预定义材质
public static class MaterialManager
{
public static readonly string LawnUuid = "lawn-material-uuid";
public static readonly string ConcreteUuid = "concrete-material-uuid";
public static readonly string Metal1Uuid = "metal1-material-uuid";
// 获取预定义材质
public static MaterialInfo GetMaterial(string uuid)
{
// 根据UUID返回对应的材质信息
return MaterialStore.Get(uuid);
}
}
15.7.2 材质在Provider中的应用
每个Provider都实现了GetSolidMats方法来指定三维模型的材质:
// 草坪Provider的材质获取
private static MaterialInfo[] GetSolidMats(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator,
Solid3d solid)
{
// 返回元素的Material属性作为三维材质
return new MaterialInfo[] {
pset.GetValue<MaterialInfo>("Material")
};
}
// 基坑Provider的材质获取(多材质)
private static MaterialInfo[] GetSolidMats(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator,
Solid3d solid)
{
// 基坑底面使用金属材质,坡面使用混凝土材质
return new MaterialInfo[] {
MaterialManager.GetMaterial(MaterialManager.Metal1Uuid),
MaterialManager.GetMaterial(MaterialManager.ConcreteUuid)
};
}
15.8 数据交换最佳实践
15.8.1 导入工作流
DWG/DXF文件 → netDxf解析 → LightCAD元素 → 场布元素转换
↓ ↓
读取图层 → 创建LcLayer → 分配到对应图层
读取线段 → 创建LcLine → 转换为场布元素
读取多段线 → 创建LcPolyLine → 识别闭合区域
读取圆弧 → 创建LcArc → 组合为复合轮廓
15.8.2 导出工作流
场布元素 → 几何数据提取 → 格式转换 → 文件写入
↓
QdLawn → Outline → DXF Polyline → .dxf
QdFence → BaseCurve→ DXF Polyline → .dxf
QdFoundation→ Outline → DXF Polyline → .dxf
↓
所有元素 → 3D Solid → OBJ/GLTF → .obj/.gltf
15.8.3 注意事项
- 坐标系统一致性:FY_Layout使用毫米为单位,导入DWG/DXF时需注意单位转换
- 图层映射:导入时需将外部图层映射到FY_Layout的标准图层命名
- 闭合性检查:导入的多段线需要检查闭合性,可使用LcCurveChangeLoop工具
- 材质兼容:导入的颜色信息需要映射到MaterialInfo系统
- 性能考虑:大文件导入时应使用异步操作,避免界面卡顿
15.9 本章小结
本章详细介绍了FY_Layout的文件格式支持和数据交换机制:
- DWG/DXF格式:通过netDxf库实现AutoCAD文件的读写兼容
- JSON序列化:使用Newtonsoft.Json实现元素属性的持久化和配置管理
- SQLite存储:通过Dapper ORM实现结构化数据的本地存储
- SVG导出:支持二维设计图的矢量图格式导出
- 三维数据:GeometryData结构支持三维模型的存储和导出
- 材质系统:MaterialInfo和MaterialManager提供统一的材质管理
- 最佳实践:导入导出工作流的规范化处理
良好的文件格式支持是CAD/BIM平台的基础能力。FY_Layout通过开放的文件格式和标准化的数据交换接口,实现了与主流设计工具的互通互联。
| 上一章:设备与模板排布系统 | 下一章:调试测试与性能优化 |