C# CAD二次開發之基本圖形

autoCAD二次開發之基本圖形(二)


本文章基礎知識:

  • C# 基礎
  • 面向對象編程
  • 事務概念

本文章開發環境:

  • autoCAD 2016
  • VS2017
  • .NET 4.5

在上一篇文章中介紹了最基本的CAD開發的概念,工具,和寫了一個Hello World程序,在這一篇文章中將做一些真正有趣,看得見的東西。我們將在CAD的圖形區域中畫上一些圖形。廢話不多說,直接進入正題。

圖形數據庫

首先一個我們需要知道的是在CAD圖形空間中畫圖,實際上就是往圖形數據庫中添加圖元。

在autoCAD中圖形數據庫佔有非常重要的位置,我們經常會跟圖形數據庫打交道。如果我們需要往圖形空間中畫圖的話,我們需要做如下的事情:

  • 獲取當前文檔的圖形數據庫
  • 打開圖形數據庫中的塊表(BlockTable)
  • 打開塊表中的塊表記錄(默認情況下有三個塊表記錄,其中ModelSpace就是我們畫圖所需要打開的)
  • 然後把圖元添加到塊表記錄中

上面出現了塊表塊表記錄的概念,那麼具體什麼是塊表塊表記錄。在圖形數據庫中其實並不只有塊表,也有層表,文字樣式表,標註樣式表,線形表等。例如對於層表來說它允許有多個層表記錄,那麼層表記錄其實就是每個圖層而已。對標註樣式表也一樣,標註樣式表記錄就是具體的每個標註樣式。

所以表和表記錄的關係就是在表中會有多個表記錄,每個表記錄就是一個具體的東西。

下圖就是autoCAD中的數據庫的簡圖

圖形數據庫圖示

下面我們以添加一條直線爲例,說明如何在CAD中添加圖形對象。

直線

autoCAD中的直線對象爲Line,它有一個無參構造函數和一個帶起點,終點的構造函數。

  • Line()
  • Line(Point3d pointer1, Point3d pointer2)

下面是Line對象的一些常用屬性:

  • public double Length { get; },獲取直線的長度
  • public override Point3d EndPoint { get; set; },獲取或設置線段的端點
  • public override Point3d StartPoint { get; set; },獲取或設置線段的起點

下面我們就使用帶起點終點的構造函數創建直線:

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace CADDemo
{
    public class AddEntity
    {
        [CommandMethod("AddLine")]
        public void AddLine()
        {
            //獲取當前文檔圖形數據庫
            Database database = HostApplicationServices.WorkingDatabase;

            //起點
            Point3d startPoint = new Point3d(0, 0, 0);
            //終點
            Point3d endPoint = new Point3d(100, 100, 0);
            //創建直線對象
            Line line = new Line(startPoint,endPoint);
            
            //使用事務打開數據庫的塊表和塊表記錄並且添加直線並提交。
            using (Transaction tr = database.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(database.BlockTableId, OpenMode.ForRead);
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
                btr.AppendEntity(line);
                tr.AddNewlyCreatedDBObject(line, true);
                tr.Commit();
            }
        }
    }
}

可以看到首先我們是獲取了一個數據庫對象,然後創建了起點和終點(Point3d結構),然後用起點和終點創建了直線對象。

using語句中打開了一個圖形數據庫的事務對象,通過事務對象來進行讀寫數據庫,在事務中一般調用事務對象中的GetObject()方法來讀取數據庫中的對象。GetObject()有兩個參數,其中第一個參數是一個ObjectId對象,ObjectId代表autoCAD中的任何對象,圖形,圖層,樣式,數據庫表和表記錄等都有一個ObjectId來唯一標識。第二個參數表示採用何種方式打開對象。例如上面代碼第一處調用GetObject()方法的地方,第一個參數爲Database對象的屬性BlockTableId返回塊表id,第二個參數爲ForRead,就是說以只讀的方式打開一個塊表。第二處調用GetObject()的地方表示以寫的方式打開塊表中的ModelSpace塊表記錄。

隨後我們調用AppendEntity()來往塊表記錄中添加剛剛我們創建的直線對象,然後調用tr.AddNewlyCreatedDBObject()來通知數據庫有更新。最後我們調用Commit()來提交事務。

注意:Point3d對象是結構,而不是類。

注意:往塊表記錄中添加圖形後必須要調用Commit()更新纔會真正被寫入數據庫。這也是基本的數據庫事務概念。

進一步封裝

其實從上面的例子中我們可以看到創建一條直線的代碼其實只有3行,而其他的都是一些千遍一律的打開塊表,塊表記錄,添加圖形,提交。所以我們可以把這些多而重複的代碼進一步封裝而不必每次都寫這麼長的代碼。

我們可以通過拓展方法的方式來進行封裝,這也是很多的一些書籍教材所用的方式,也是最方便使用的方式。

public static ObjectId AddEntity(this Database database,Entity entity)
{
    ObjectId objectId = new ObjectId();
    using (Transaction tr = database.TransactionManager.StartTransaction())
    {
        BlockTable bt = (BlockTable)tr.GetObject(database.BlockTableId, OpenMode.ForRead);
        BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
        objectId = btr.AppendEntity(entity);
        tr.AddNewlyCreatedDBObject(entity, true);
        tr.Commit();
    }
    return objectId;
}

上面的代碼就爲Database對象添加了一個名爲AddEntity的拓展方法。其中AddEntity方法接收一個Entity類型的參數,Entity是所有圖形對象的父類。

添加了上面的拓展方法後,畫直線的代碼就簡化成:

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace CADDemo
{
    public class AddEntity
    {
        [CommandMethod("AddLine")]
        public void AddLine()
        {
            Database database = HostApplicationServices.WorkingDatabase;

            Point3d startPoint = new Point3d(0, 0, 0);
            Point3d endPoint = new Point3d(100, 100, 0);
            Line line = new Line(startPoint,endPoint);

            database.AddEntity(line);
        }
    }
}

在以後的文章我們也將繼續沿用該拓展方法來添加圖形對象。

圓和圓弧

注意:對於下面所有的角度單位都是使用弧度制

首先我們來看一下圓,在autoCAD中圓對象爲Circle類型,該對象的構造函數有兩個重載版本

  • Circle()
  • Circle(Point3d center, Vector3d normal, double radius)

第一個構造函數創建一個圓心爲原點,半徑爲0,法向量爲(0,0,1)的圓。第二個構造函數通過指定圓心,法向量,半徑來創建圓。其中對於一般的X,Y平面法向量爲(0,0,1)

我們還可以利用幾何對象來通過三點創建圓和圓弧,關於幾何對象我們將在下一篇文章中講解。

其中Circle類型的一些常用屬性如下:

  • public Point3d Center { get; set; },獲取或設置圓的圓心
  • public double Radius { get; set; },獲取或設置圓的半徑

下面代碼創建一個圓心在原點,半徑爲100的圓:

[CommandMethod("AddCircle")]
public void AddCircle()
{
    Database database = HostApplicationServices.WorkingDatabase;

    Point3d center = new Point3d();
    double radius = 100;

    Circle circle = new Circle(center, new Vector3d(0, 0, 1), radius);

    database.AddEntity(circle);
}

圓弧對象爲Arc類型,該對象的構造函數有三個重載版本

  • Arc()
  • Arc(Point3d center, double radius, double startAngle, double endAngle)
  • Arc(Point3d center, Vector3d normal, double radius, double startAngle, double endAngle)

其中第一個構造函數爲無參的默認構造函數。第二個構造函數通過指定圓心,半徑,起始角度,端點角度來創建圓弧對象。第三個構造函數只比第二個構造函數多了一個平面法向量參數。一般我們使用第二個構造函數就可以了。

Arc類型的一些常用屬性如下:

  • public double TotalAngle { get; },獲取圓弧的總角度
  • public double EndAngle { get; set; },獲取或設置端點角度
  • public double StartAngle { get; set; },獲取或設置起點角度
  • public double Radius { get; set; },獲取或設置圓弧半徑
  • public Point3d Center { get; set; },獲取或設置圓弧圓心

下面的代碼創建一個圓心在原點,半徑爲100,被座標系截取在第一象限的圓弧:

[CommandMethod("AddArc")]
public void AddArc()
{
    Database database = HostApplicationServices.WorkingDatabase;

    Point3d center = new Point3d();
    double radius = 100;
    double startAngle = 0;
    double endAngle = System.Math.PI / 2;

    Arc arc = new Arc(center, radius, startAngle, endAngle);

    database.AddEntity(arc);
}

圓弧的起始和端點角度有一個需要注意的地方,autoCAD將從起始角度逆時針畫到端點角度。舉上面畫的第一象限圓弧爲例,如果把起始角度換成pi/2,把端點角度換成0,那麼畫出來的圓弧將會從Y軸正半軸逆時針畫到X軸正半軸,跨越2,3,4象限,剛好跟上面的例子互補。

多段線

autoCAD中多段線對象爲Polyline類型,該對象的構造函數有兩個重載版本

  • Polyline()
  • public Polyline(int vertices)

其中第一個是無參構造函數,第二個構造函數通過指定一個固定的頂點數來創建多段線。下面我們都是使用第一個無參構造函數來創建多段線。

下面列出Polyline類型的一些常用屬性:

  • public int NumberOfVertices { get; },獲取多段線的頂點數
  • public double Length { get; },獲取多段線的長度
  • public bool Closed { get; set; },獲取多段線是否閉合

下面我們來說一下Polyline類型的一些常用的方法,其中AddVertexAt(int index, Point2d pt, double bulge, double startWidth, double endWidth)方法爲多段線添加頂點,其中第一個參數是頂點的索引,索引從零開始,也就是說索引爲0就是第一個頂點,爲1就是第二個頂點。方法的第二個參數是頂點的點座標,第三個參數是該頂點的凸度(下面將說明凸度的概念),第四和第五個參數是起點和終點寬度一般爲0就好了。

方法GetPoint2dAt(int index)用來返回在指定索引處的點座標;GetBulgeAt(int index)方法返回在指定索引處的凸度;RemoveVertexAt(int index)方法移除指定索引的頂點;

我們用多段線畫一個正方形的代碼如下:

[CommandMethod("AddPolyline")]
public void AddPolyline()
{
    Database database = HostApplicationServices.WorkingDatabase;

    Point2d p1 = new Point2d(0, 0);
    Point2d p2 = new Point2d(0, 100);
    Point2d p3 = new Point2d(100, 100);
    Point2d p4 = new Point2d(100, 0);

    Polyline polyline = new Polyline();
    polyline.AddVertexAt(0, p1, 0, 0, 0);
    polyline.AddVertexAt(1, p2, 0, 0, 0);
    polyline.AddVertexAt(2, p3, 0, 0, 0);
    polyline.AddVertexAt(3, p4, 0, 0, 0);
    polyline.AddVertexAt(4, p1, 0, 0, 0);

    database.AddEntity(polyline);
}

上面代碼調用Polyline類型的默認構造函數後通過調用AddVertexAt方法來往多段線添加頂點,其中頂點之間是直線連接(因爲凸度都爲0)。

凸度

好了,那麼如果多段線需要有圓弧的話需要怎麼畫?關鍵就在AddVertexAt的第三個參數(凸度)。凸度定義爲該頂點和下一個頂點的圓弧的起始和端點角度差的四分一正切值,即tan(△angle/4))),其中△angle就是圓弧的起始和端點角度差。

其中凸度有正負之分,正的凸度值表示從該頂點畫出的圓弧在多段線的前進方向的右側,負的凸度值表示從該頂點畫出的圓弧在多段線前進方向的左側。

如果我們在上面用多段線畫的正方形的上邊畫成一個半圓,我們可以作如下修改。

[CommandMethod("AddPolyline")]
public void AddPolyline()
{
    Database database = HostApplicationServices.WorkingDatabase;

    Point2d p1 = new Point2d(0, 0);
    Point2d p2 = new Point2d(0, 100);
    Point2d p3 = new Point2d(100, 100);
    Point2d p4 = new Point2d(100, 0);

    Polyline polyline = new Polyline();
    polyline.AddVertexAt(0, p1, 0, 0, 0);
    //畫從p2到p3的半圓
    polyline.AddVertexAt(1, p2, Math.Tan((0-Math.PI)/4), 0, 0);
    polyline.AddVertexAt(2, p3, 0, 0, 0);
    polyline.AddVertexAt(3, p4, 0, 0, 0);
    polyline.AddVertexAt(4, p1, 0, 0, 0);

    database.AddEntity(polyline);
}

可以看到上面只在添加第二個頂點的AddVertexAt函數中的第三個參數改爲Math.Tan((0-Math.PI)/4),也就是我們要求半圓的起始角度和端點角度的差值的四分之一,然後再對其求tan正切值,這樣來求出凸度。注意:從上面的式子中求出的凸度值爲負數,所以我們的半圓應該是在正方形外部。如果凸度值爲正的話半圓會在正方形的內部。

面域

面域是一個封閉的的區域,我們可以利用面域來求不規則圖形的面積,周長,而且還可以利用面域來跟其他面域作布爾運算。

面域在autoCAD中用Region類型來表示,Region類型的創建跟上面的對象有點不一樣的,它是通過Region類的一個靜態函數public static DBObjectCollection CreateFromCurves(DBObjectCollection curveSegments)來創建。

可以看到該方法的參數是一個DBObjectCollection對象,它是一個表示cad對象的集合,該類實現IList接口,所以我們可以當它是集合來使用就好了,在這裏就是用集合中的對象來創建閉合的面域。方法的返回值也是一個DBObjectCollection,因爲如果參數中的對象可以圍成多個閉合的面域那麼方法也會一一返回。

Region類型常用的屬性如下:

  • public virtual double Area { get; },返回面域的面積
  • public virtual double Perimeter { get; },返回面域的周長

下面的代碼實例創建一個正方形的面域:

[CommandMethod("AddRegion")]
public void AddRegion()
{
    Database database = HostApplicationServices.WorkingDatabase;

    Point3d p1 = new Point3d(0, 0, 0);
    Point3d p2 = new Point3d(0, 100, 0);
    Point3d p3 = new Point3d(100, 100, 0);
    Point3d p4 = new Point3d(100, 0, 0);

    Line line1 = new Line(p1, p2);
    Line line2 = new Line(p2, p3);
    Line line3 = new Line(p3, p4);
    Line line4 = new Line(p4, p1);

    DBObjectCollection collection = new DBObjectCollection()
    {
        line1,
        line2,
        line3,
        line4
    };
    Region region = (Region)Region.CreateFromCurves(collection)[0];

    database.AddEntity(region);
}

在上面的事例中,因爲由collection圍成的正方形面域只可能有一個,所以在下面調用CreateFromCurves時我們就直接返回第一個元素並且強制轉換爲Region類型。

面域還可以進行布爾運算,在面域之間求並,交和差,下面就是一個例子:

region.BooleanOperation(BooleanOperationType.BoolSubtract, region2);

上面的例子就是對region2作布爾減運算,要作布爾運算可以調用Region類型的BooleanOperation方法。其中第一個參數是布爾運算的類型枚舉,該枚舉共有三個值分別爲BoolUnite(並),BoolIntersect(交),BoolSubtract(差)。第二個參數爲要作布爾運算的另一個面域。運算的結果賦給調用該方法的面域。

文字

文字在autoCAD中用類型DBText來表示,該類型只有一個無參構造函數Arc()

DBText常用的屬性如下:

  • public string TextString { get; set; },返回或設置文字顯示的字符串
  • public Point3d Position { get; set; },返回或設置文字的顯示位置
  • public Point3d AlignmentPoint { get; set; },返回或設置文字的對齊點位置
  • public double Height { get; set; },返回或設置文字高度
  • public double Rotation { get; set; },返回或設置文字的旋轉角度
  • public TextHorizontalMode HorizontalMode { get; set; },返回或設置文字的水平對齊方式
  • public TextVerticalMode VerticalMode { get; set; },返回或設置文字的垂直對齊方式
  • public AttachmentPoint Justify { get; set; },返回或設置文字對齊方式

上面的PositionAlignmentPoint屬性一般設置爲同一個點就可以了(之前測試過如果不同會報錯,目前還不清楚具體原因)。

還有上面列出的屬性中共有3種對齊方式,其中水平對齊和垂直對齊應該都能理解,這裏就說一下最後一個Justify。可以看到該屬性接受一個AttachmentPoint類型,該類型是一個枚舉類型,因爲該類型的枚舉太多所以我這列出兩個來進行舉例。其中AttachmentPoint.BottomRight表示把文字的右下角對齊到AlignmentPoint屬性設置的點;AttachmentPoint.MiddleCenter則表示把文字的中心對齊到AlignmentPoint屬性設置的點。

下面的例子說明如何創建一個簡單的文字對象:

[CommandMethod("AddText")]
public void AddText()
{
    Database database = HostApplicationServices.WorkingDatabase;

    DBText text = new DBText();
    text.TextString = "Hello World";
    text.Position = new Point3d(0,0,0);
    text.AlignmentPoint = text.Position;
    text.Height = 150;
    text.HorizontalMode = TextHorizontalMode.TextCenter;
    text.VerticalMode = TextVerticalMode.TextVerticalMid;
    text.Justify = AttachmentPoint.MiddleCenter;

    database.AddEntity(text);
}

總結

上面就是一些常用的圖形對象,更多的其他圖形對象可以查看AutoCAD的官方文檔,裏面有完整的API文檔。

下一篇文章將爲大家介紹一些常用的幾何對象來進行輔助計算。

該篇文章是根據本人已有的知識來寫,如有不對或者更好的方式可以告訴我。

本人的公衆號,歡迎有興趣的關注下

公衆號ChivaStudio

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章