CAD二次开发 C# 实现简单的线路绘制

91 阅读3分钟

写这篇文章主要是为了记录一下实现功能的思路,如果有更好的思路或者代码错误欢迎大家指正
功能场景大概是:用户选点画圆-选点画圆-两点画线相连,循环下去直到用户esc取消。
扩展功能:实现撤销、标注线长度。
实现思路:用户选择起始点先绘制第一个圆,之后进入循环让用户选点,根据最后两次选点绘制线段。为了方便区分圆和线分别放在不同的图层里。

需要注意的是为了能够及时看到绘制效果,当把cad对象插入到模型空间之后记得到提交事务再开启新事务进行后续绘制。
撤销的话,当然是以用户选点操作为核心,记录下和本次选点到下次选点这中间里插入到模型空间的对象,当撤销时就删除和最后一个点相关的对象。

我相信这个场景应该还是简单容易实现的,所以提供了完整的代码,以后如果有相对复杂的功能实现,只会提供核心部分代码和我的实现思路,剩余由读者自己完善。授人予鱼不如授人予渔

下面是主要的实现思路:

public void Draw()
{
    Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    DocumentLock docLock = doc.LockDocument();
    Transaction transaction = db.TransactionManager.StartTransaction();
    Point3d? baseP = GetPoint();
    if (!baseP.HasValue) return;

    double radius = 0.5;//圆半径
    bool hiddenCircle = false;//是否绘制圆
    bool hiddenLine = false;//是否绘制线
    Polyline polyline1 = GetFullCircle(baseP.Value, radius);
    if (hiddenCircle) InsertDBObject(transaction, db, "圆", polyline1);
    //记录下选点相关的绘制数据 为了撤销
    List<DrawModel> vlog = new List<DrawModel>();
    while (true)
    {
        List<Entity> temp = new List<Entity>();
        double h1 = 2.0;//字高
        //设置文本距离线的距离
        double mtextDDJ = h1 + 0.5;
        bool isUndo = false;
        Point3d? selectP = GetPointUndo("选择插入点,", baseP.Value, ref isUndo);
        if (isUndo)
        {
            if (vlog.Count == 0) continue;//已经撤销到最初的绘制状态
            foreach (var item in vlog.LastOrDefault().ents)
            {
                DBObject obj = transaction.GetObject(item.ObjectId, OpenMode.ForWrite);
                if (obj != null)
                {
                    obj.Erase();
                }
            }
            baseP = vlog.LastOrDefault().point;//切换到最后一个点位置
            vlog.RemoveAt(vlog.Count - 1);
            transaction.Commit();
            transaction = db.TransactionManager.StartTransaction();
            continue;
        }
        if (!selectP.HasValue) break;//用户没有选点 esc操作
        if(!hiddenCircle)
        {
            polyline1 = GetFullCircle(baseP.Value, radius);
            InsertDBObject(transaction, db, "圆", polyline1);
            temp.Add(polyline1);
            transaction.Commit();
            transaction = db.TransactionManager.StartTransaction();
        }
        Line line = new Line(baseP.Value, selectP.Value);
        if(!hiddenLine)
        {
            InsertDBObject(transaction, db, "线", line);
            temp.Add(line);
        }
        Point3d midPoint3d = new Point3d((line.StartPoint.X + line.EndPoint.X) / 2.0, (line.StartPoint.Y + line.EndPoint.Y) / 2.0, 0.0);
        if (!hiddenCircle)
        {
            Polyline polyline2 = GetFullCircle(selectP.Value, radius);
            InsertDBObject(transaction, db, "圆", polyline2);
            temp.Add(polyline2);
        }
        string dj = Math.Ceiling(Math.Round(line.Length, 4)).ToString("0") + "m";
        double mangle = line.Angle;
        //线角度是二三象限时 注意调整文本的角度
        if(mangle > Math.PI/2 && mangle < Math.PI)
        {//90-180度 
            mangle += Math.PI;
        }
        else if(mangle >= Math.PI && mangle < Math.PI * 1.5)
        {//180-270度
            mangle -= Math.PI;
        }
        Entity mt1 = DrawMText(transaction, db, dj, midPoint3d.Add(Vector3d.XAxis.RotateBy(line.Angle + Math.PI / 2, Vector3d.ZAxis) * mtextDDJ), h1, "长度", mangle);
        temp.Add(mt1);
        transaction.Commit();
        transaction = db.TransactionManager.StartTransaction();

        vlog.Add(new DrawModel() { point = baseP.Value, ents = temp });
        baseP = selectP;
    }
    transaction.Commit();
    transaction.Dispose();
    if (docLock != null)
    {
        docLock.Dispose();
    }
}

下面是功能实现用到的工具方法:

/// <summary>
/// 绘制文本
/// </summary>
/// <param name="transaction"></param>
/// <param name="db"></param>
/// <param name="textString">文本内容</param>
/// <param name="point"></param>
/// <param name="TextHeight">字高</param>
/// <param name="layerName">图层</param>
/// <param name="rotation">角度</param>
/// <returns></returns>
public Entity DrawMText(Transaction transaction, Database db, string textString, Point3d point, double TextHeight, string layerName, double? rotation)
{
    MText txt = new MText();
    txt.TextHeight = TextHeight;
    txt.Location = point;
    InsertDBObject(transaction, db, layerName, txt);
    if (!txt.IsWriteEnabled) txt.UpgradeOpen();

    txt.Color = Autodesk.AutoCAD.Colors.Color.FromColorIndex(Autodesk.AutoCAD.Colors.ColorMethod.ByLayer, 256);
    txt.Attachment = AttachmentPoint.MiddleCenter;
    txt.Rotation = rotation.Value;
    txt.Contents = textString;
    txt.Visible = true;
    return txt;
}
private Point3d? GetPoint()
{
    Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    PromptPointOptions prPointOptions = new PromptPointOptions("选择插入点");
    PromptPointResult prPointRes = doc.Editor.GetPoint(prPointOptions);
    if (prPointRes.Status == PromptStatus.OK)
    {
        return prPointRes.Value;
    }
    return null;
}
/// <summary>
/// 绘制圆(用polyline表示实心效果
/// </summary>
/// <param name="center"></param>
/// <param name="radius"></param>
/// <returns></returns>
private Polyline GetFullCircle(Point3d center, double radius)
{
    Polyline pline = new Polyline();
    pline.AddVertexAt(0, center.Add(Vector3d.YAxis * radius / 2).toPoint2d(), 1, radius, radius);
    pline.AddVertexAt(1, center.Add(Vector3d.YAxis * -radius / 2).toPoint2d(), 1, radius, radius);
    pline.AddVertexAt(2, center.Add(Vector3d.YAxis * radius / 2).toPoint2d(), 0, 0, 0);
    return pline;
}
//选点(支持撤销
private Point3d? GetPointUndo(string mess, Point3d basePoint, ref bool isUndo)
{
    Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    PromptPointOptions prPointOptions = new PromptPointOptions("");
    prPointOptions.UseDashedLine = true;
    prPointOptions.UseBasePoint = true;
    prPointOptions.BasePoint = basePoint;
    prPointOptions.SetMessageAndKeywords("\n" + mess + "[Undo]: ", "Undo");
    PromptPointResult prPointRes = doc.Editor.GetPoint(prPointOptions);
    if (prPointRes.Status == PromptStatus.Keyword && prPointRes.StringResult == "Undo")
    {
        isUndo = true;
        return null;
    }
    if (prPointRes.Status == PromptStatus.OK)
    {
        return prPointRes.Value;
    }
    return null;
}
//插入cad对象到模型空间
private void InsertDBObject(Transaction transaction, Database db, string layerName, params Entity[] objs)
{
    CreateLayer(transaction, db, layerName, false, false);
    BlockTable bt = transaction.GetObject(db.BlockTableId, OpenMode.ForWrite) as BlockTable;
    BlockTableRecord btr = transaction.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
    foreach (Entity ent in objs)
    {
        ent.Layer = layerName;
        ObjectId oi = btr.AppendEntity(ent);
        transaction.AddNewlyCreatedDBObject(ent, true);
    }
}
//创建图层
private void CreateLayer(Transaction transaction, Database db, string layerName)
{
    LayerTable lt = transaction.GetObject(db.LayerTableId, OpenMode.ForWrite) as LayerTable;
    if (!string.IsNullOrEmpty(layerName) && !lt.Has(layerName))
    {
        LayerTableRecord ltr = new LayerTableRecord();
        ltr.Name = layerName;
        lt.Add(ltr);
        transaction.AddNewlyCreatedDBObject(ltr, true);
    }
}