znlgis 博客

GIS开发与技术分享

第十四章:文件格式与数据交换

14.1 文件格式支持概述

14.1.1 LightCAD支持的格式

格式 读取 写入 说明
LightCAD原生格式 完整保留所有数据
DWG AutoCAD绘图格式(通过ODA SDK)
DXF AutoCAD交换格式(通过netDxf)
SketchUp (.skp) SketchUp模型格式
SVG - 可缩放矢量图形

14.1.2 模块结构

LightCAD.ImportAndExport/
├── DwgImporter.cs           # DWG导入
├── DwgExporter.cs           # DWG导出
├── DxfImporter.cs           # DXF导入
├── DxfExporter.cs           # DXF导出
├── SketchUpImporter.cs      # SketchUp导入
├── SketchUpExporter.cs      # SketchUp导出
└── FormatConverter.cs       # 格式转换器

src/Libs/
├── LightCAD.DwgReaderWrite/ # DWG读写库
└── ODA SDK DLLs             # Open Design Alliance SDK

src/netDxf/                  # DXF处理库(源码)
├── DxfDocument.cs           # DXF文档
├── DxfReader.cs             # DXF读取器
├── DxfWriter.cs             # DXF写入器
└── Entities/                # DXF实体映射

14.2 DWG格式支持

14.2.1 DWG格式概述

DWG(Drawing)是AutoCAD的原生文件格式,也是CAD行业最广泛使用的文件格式。LightCAD通过ODA(Open Design Alliance)SDK实现了对DWG格式的读写支持。

14.2.2 DWG导入

public class DwgImporter
{
    /// <summary>
    /// 导入DWG文件
    /// </summary>
    public LcDocument Import(string filePath)
    {
        var document = new LcDocument();

        using (var database = new OdDatabase())
        {
            // 读取DWG文件
            database.ReadDwgFile(filePath);

            // 导入图层
            ImportLayers(database, document);

            // 导入线型
            ImportLineTypes(database, document);

            // 导入文字样式
            ImportTextStyles(database, document);

            // 导入标注样式
            ImportDimStyles(database, document);

            // 导入块定义
            ImportBlocks(database, document);

            // 导入模型空间中的实体
            ImportEntities(database, document);
        }

        return document;
    }

    /// <summary>
    /// 导入图层
    /// </summary>
    private void ImportLayers(OdDatabase database, LcDocument document)
    {
        var layerTable = database.LayerTable;

        foreach (var layerId in layerTable)
        {
            var layer = layerId.GetObject() as OdLayerTableRecord;
            if (layer != null)
            {
                document.Layers.Create(layer.Name);
                var lcLayer = document.Layers.Get(layer.Name);
                lcLayer.Color = ConvertColor(layer.Color);
                lcLayer.IsVisible = !layer.IsFrozen && !layer.IsOff;
                lcLayer.IsLocked = layer.IsLocked;
                lcLayer.LineType = layer.LinetypeName;
            }
        }
    }

    /// <summary>
    /// 导入实体
    /// </summary>
    private void ImportEntities(OdDatabase database, LcDocument document)
    {
        var modelSpace = database.ModelSpace;

        foreach (var entityId in modelSpace)
        {
            var entity = entityId.GetObject();
            var lcEntity = ConvertEntity(entity);

            if (lcEntity != null)
            {
                lcEntity.LayerName = entity.Layer;
                lcEntity.Color = ConvertColor(entity.Color);
                document.Entities.Add(lcEntity);
            }
        }
    }

    /// <summary>
    /// 转换DWG实体为LightCAD实体
    /// </summary>
    private LcEntity ConvertEntity(OdEntity odEntity)
    {
        return odEntity switch
        {
            OdLine line => new LcLine
            {
                StartPoint = ConvertPoint(line.StartPoint),
                EndPoint = ConvertPoint(line.EndPoint)
            },
            OdCircle circle => new LcCircle
            {
                Center = ConvertPoint(circle.Center).ToPoint2d(),
                Radius = circle.Radius
            },
            OdArc arc => new LcArc
            {
                Center = ConvertPoint(arc.Center).ToPoint2d(),
                Radius = arc.Radius,
                StartAngle = arc.StartAngle,
                EndAngle = arc.EndAngle
            },
            OdPolyline polyline => ConvertPolyline(polyline),
            OdText text => ConvertText(text),
            OdDimension dim => ConvertDimension(dim),
            OdBlockReference blockRef => ConvertBlockReference(blockRef),
            _ => null
        };
    }
}

14.2.3 DWG导出

public class DwgExporter
{
    /// <summary>
    /// 导出为DWG文件
    /// </summary>
    public void Export(LcDocument document, string filePath,
                       DwgVersion version = DwgVersion.AC1032)
    {
        using (var database = new OdDatabase())
        {
            // 设置DWG版本
            database.DwgFileVersion = version;

            // 导出图层
            ExportLayers(document, database);

            // 导出实体
            ExportEntities(document, database);

            // 导出块定义
            ExportBlocks(document, database);

            // 写入文件
            database.SaveAs(filePath, version);
        }
    }

    /// <summary>
    /// 转换LightCAD实体为DWG实体
    /// </summary>
    private OdEntity ConvertToOdEntity(LcEntity entity)
    {
        return entity switch
        {
            LcLine line => new OdLine
            {
                StartPoint = ConvertToOdPoint(line.StartPoint),
                EndPoint = ConvertToOdPoint(line.EndPoint)
            },
            LcCircle circle => new OdCircle
            {
                Center = ConvertToOdPoint3d(circle.Center),
                Radius = circle.Radius
            },
            LcArc arc => new OdArc
            {
                Center = ConvertToOdPoint3d(arc.Center),
                Radius = arc.Radius,
                StartAngle = arc.StartAngle,
                EndAngle = arc.EndAngle
            },
            _ => null
        };
    }
}

public enum DwgVersion
{
    AC1015,  // AutoCAD 2000
    AC1018,  // AutoCAD 2004
    AC1021,  // AutoCAD 2007
    AC1024,  // AutoCAD 2010
    AC1027,  // AutoCAD 2013
    AC1032   // AutoCAD 2018+
}

14.3 DXF格式支持

14.3.1 DXF格式概述

DXF(Drawing Exchange Format)是AutoCAD的文本交换格式。相比DWG,DXF是开放的文本格式,更容易解析和生成。LightCAD使用netDxf库来处理DXF文件。

14.3.2 DXF导入

public class DxfImporter
{
    /// <summary>
    /// 导入DXF文件
    /// </summary>
    public LcDocument Import(string filePath)
    {
        var dxfDoc = netDxf.DxfDocument.Load(filePath);
        var document = new LcDocument();

        // 导入图层
        foreach (var layer in dxfDoc.Layers)
        {
            var lcLayer = document.Layers.Create(layer.Name);
            lcLayer.Color = ConvertDxfColor(layer.Color);
            lcLayer.IsVisible = layer.IsVisible;
        }

        // 导入实体
        ImportLines(dxfDoc, document);
        ImportCircles(dxfDoc, document);
        ImportArcs(dxfDoc, document);
        ImportPolylines(dxfDoc, document);
        ImportTexts(dxfDoc, document);
        ImportDimensions(dxfDoc, document);
        ImportInserts(dxfDoc, document);

        return document;
    }

    private void ImportLines(netDxf.DxfDocument dxfDoc, LcDocument document)
    {
        foreach (var dxfLine in dxfDoc.Entities.Lines)
        {
            var line = new LcLine
            {
                StartPoint = new Point2d(
                    dxfLine.StartPoint.X, dxfLine.StartPoint.Y),
                EndPoint = new Point2d(
                    dxfLine.EndPoint.X, dxfLine.EndPoint.Y),
                LayerName = dxfLine.Layer.Name,
                Color = ConvertDxfColor(dxfLine.Color)
            };
            document.Entities.Add(line);
        }
    }

    private void ImportPolylines(netDxf.DxfDocument dxfDoc,
                                  LcDocument document)
    {
        foreach (var dxfPoly in dxfDoc.Entities.LightWeightPolylines)
        {
            var polyline = new LcPolyline
            {
                IsClosed = dxfPoly.IsClosed,
                LayerName = dxfPoly.Layer.Name
            };

            foreach (var vertex in dxfPoly.Vertexes)
            {
                polyline.AddVertex(
                    new Point2d(vertex.Position.X, vertex.Position.Y),
                    vertex.Bulge);
            }

            document.Entities.Add(polyline);
        }
    }
}

14.3.3 DXF导出

public class DxfExporter
{
    /// <summary>
    /// 导出为DXF文件
    /// </summary>
    public void Export(LcDocument document, string filePath,
                       DxfVersion version = DxfVersion.AutoCad2018)
    {
        var dxfDoc = new netDxf.DxfDocument(
            (netDxf.Header.DxfVersion)version);

        // 导出图层
        foreach (var layerName in document.Layers.AllLayerNames)
        {
            var lcLayer = document.Layers.Get(layerName);
            var dxfLayer = new netDxf.Tables.Layer(layerName)
            {
                Color = ConvertToAciColor(lcLayer.Color),
                IsVisible = lcLayer.IsVisible
            };
            dxfDoc.Layers.Add(dxfLayer);
        }

        // 导出实体
        foreach (var entity in document.Entities)
        {
            var dxfEntity = ConvertToDxfEntity(entity);
            if (dxfEntity != null)
            {
                dxfDoc.Entities.Add(dxfEntity);
            }
        }

        // 保存文件
        dxfDoc.Save(filePath);
    }
}

14.4 SketchUp格式支持

14.4.1 SketchUp导入

public class SketchUpImporter
{
    /// <summary>
    /// 导入SketchUp文件
    /// </summary>
    public LcDocument Import(string filePath)
    {
        var document = new LcDocument();

        // 解析SKP文件
        using (var reader = new SkpReader(filePath))
        {
            // 导入材质
            foreach (var material in reader.Materials)
            {
                ImportMaterial(material, document);
            }

            // 导入组件定义
            foreach (var component in reader.ComponentDefinitions)
            {
                ImportComponent(component, document);
            }

            // 导入几何体
            foreach (var face in reader.Faces)
            {
                ImportFace(face, document);
            }

            foreach (var edge in reader.Edges)
            {
                ImportEdge(edge, document);
            }
        }

        return document;
    }

    /// <summary>
    /// 导入SketchUp面(转换为网格体)
    /// </summary>
    private void ImportFace(SkpFace face, LcDocument document)
    {
        var mesh = new LcMesh3d();

        // 转换顶点
        foreach (var vertex in face.Vertices)
        {
            mesh.Vertices.Add(new Point3d(
                vertex.X, vertex.Y, vertex.Z));
        }

        // 转换三角面
        var triangulated = face.Triangulate();
        foreach (var tri in triangulated)
        {
            mesh.FaceIndices.Add(tri.Index0);
            mesh.FaceIndices.Add(tri.Index1);
            mesh.FaceIndices.Add(tri.Index2);
        }

        mesh.RecalculateNormals();

        if (face.Material != null)
        {
            mesh.Material = new MaterialInfo
            {
                Name = face.Material.Name,
                DiffuseColor = ConvertSkpColor(face.Material.Color)
            };
        }

        document.Entities.Add(mesh);
    }
}

14.5 原生格式

14.5.1 LightCAD文件格式设计

LightCAD的原生文件格式采用二进制格式,能够完整保存所有CAD数据:

public class NativeFormatSerializer
{
    private const string MAGIC = "LCAD";
    private const int VERSION = 1;

    /// <summary>
    /// 保存文档
    /// </summary>
    public void Save(LcDocument document, string filePath)
    {
        using var stream = File.Create(filePath);
        using var writer = new BinaryWriter(stream);

        // 文件头
        writer.Write(MAGIC.ToCharArray());
        writer.Write(VERSION);
        writer.Write(DateTime.UtcNow.ToBinary());

        // 文档元数据
        writer.Write(document.Name ?? "");
        writer.Write(document.Guid.ToString());

        // 单位设置
        WriteUnitSettings(writer, document.Units);

        // 图层表
        var layers = document.Layers.GetAll();
        writer.Write(layers.Count);
        foreach (var layer in layers)
        {
            WriteLayer(writer, layer);
        }

        // 块定义
        var blocks = document.Blocks.GetAll();
        writer.Write(blocks.Count);
        foreach (var block in blocks)
        {
            WriteBlock(writer, block);
        }

        // 实体
        var entities = document.Entities.ToList();
        writer.Write(entities.Count);
        foreach (var entity in entities)
        {
            WriteEntity(writer, entity);
        }
    }

    /// <summary>
    /// 加载文档
    /// </summary>
    public LcDocument Load(string filePath)
    {
        using var stream = File.OpenRead(filePath);
        using var reader = new BinaryReader(stream);

        // 验证文件头
        var magic = new string(reader.ReadChars(4));
        if (magic != MAGIC)
            throw new InvalidDataException("无效的LightCAD文件");

        var version = reader.ReadInt32();
        var timestamp = DateTime.FromBinary(reader.ReadInt64());

        var document = new LcDocument
        {
            Name = reader.ReadString(),
            Guid = new LcGuid(reader.ReadString())
        };

        // 读取单位设置
        document.Units = ReadUnitSettings(reader);

        // 读取图层
        var layerCount = reader.ReadInt32();
        for (int i = 0; i < layerCount; i++)
        {
            ReadLayer(reader, document);
        }

        // 读取块定义
        var blockCount = reader.ReadInt32();
        for (int i = 0; i < blockCount; i++)
        {
            ReadBlock(reader, document);
        }

        // 读取实体
        var entityCount = reader.ReadInt32();
        for (int i = 0; i < entityCount; i++)
        {
            var entity = ReadEntity(reader);
            if (entity != null)
            {
                document.Entities.Add(entity);
            }
        }

        return document;
    }

    /// <summary>
    /// 写入实体(带类型标识)
    /// </summary>
    private void WriteEntity(BinaryWriter writer, LcEntity entity)
    {
        // 写入类型标识
        writer.Write(entity.TypeName);

        // 写入通用属性
        writer.Write(entity.Handle);
        writer.Write(entity.Guid.ToString());
        writer.Write(entity.LayerName);
        WriteColor(writer, entity.Color);
        writer.Write(entity.LineType ?? "");
        writer.Write(entity.LineWeight);
        writer.Write(entity.IsVisible);

        // 写入特定属性
        switch (entity)
        {
            case LcLine line:
                WritePoint2d(writer, line.StartPoint);
                WritePoint2d(writer, line.EndPoint);
                break;
            case LcCircle circle:
                WritePoint2d(writer, circle.Center);
                writer.Write(circle.Radius);
                break;
            case LcArc arc:
                WritePoint2d(writer, arc.Center);
                writer.Write(arc.Radius);
                writer.Write(arc.StartAngle);
                writer.Write(arc.EndAngle);
                writer.Write(arc.IsCounterClockwise);
                break;
            // ... 其他实体类型
        }
    }
}

14.6 格式转换工具

14.6.1 统一转换接口

public class FormatConverter
{
    /// <summary>
    /// 通用文件导入
    /// </summary>
    public LcDocument Import(string filePath)
    {
        var extension = Path.GetExtension(filePath).ToLower();

        return extension switch
        {
            ".dwg" => new DwgImporter().Import(filePath),
            ".dxf" => new DxfImporter().Import(filePath),
            ".skp" => new SketchUpImporter().Import(filePath),
            ".lcad" => new NativeFormatSerializer().Load(filePath),
            _ => throw new NotSupportedException(
                $"不支持的文件格式:{extension}")
        };
    }

    /// <summary>
    /// 通用文件导出
    /// </summary>
    public void Export(LcDocument document, string filePath)
    {
        var extension = Path.GetExtension(filePath).ToLower();

        switch (extension)
        {
            case ".dwg":
                new DwgExporter().Export(document, filePath);
                break;
            case ".dxf":
                new DxfExporter().Export(document, filePath);
                break;
            case ".lcad":
                new NativeFormatSerializer().Save(document, filePath);
                break;
            default:
                throw new NotSupportedException(
                    $"不支持的导出格式:{extension}");
        }
    }

    /// <summary>
    /// 批量格式转换
    /// </summary>
    public void BatchConvert(string inputDir, string outputDir,
                              string inputFormat, string outputFormat)
    {
        var files = Directory.GetFiles(inputDir, $"*.{inputFormat}");

        foreach (var file in files)
        {
            var document = Import(file);
            var outputPath = Path.Combine(outputDir,
                Path.ChangeExtension(
                    Path.GetFileName(file), outputFormat));
            Export(document, outputPath);
        }
    }
}

14.7 本章小结

本章详细介绍了LightCAD的文件格式支持和数据交换能力。LightCAD通过ODA SDK实现了DWG格式的完整读写,通过netDxf库支持DXF格式,还支持SketchUp格式的导入导出。原生的LightCAD文件格式采用高效的二进制编码,能够完整保存所有CAD数据。统一的格式转换接口为用户提供了便捷的文件操作体验。


上一章第十三章:用户界面框架

下一章第十五章:插件系统与扩展开发