AutoCad 二次開發 文字鏡像
在autocad中如果使用Mirror命令把塊參照給鏡像了(最終得到一個對稱的塊),塊里面的文字包括DBText和MText以及標注上面的文字都會被對稱,變得不易閱讀。而在單個字體實體和標注實體鏡像的時候只要設置系統變量mirrtext為0鏡像后的文字就不會與原文字對稱變成我們未學習過的文字了。
所以我們在鏡像塊的時候就可以先把塊炸開是用快捷鍵X,或者輸入explode,然后在使用鏡像命令。之后在把對稱后的實體集合組成一個新的塊。不過這樣操作十分的繁瑣,我覺得其中這樣做的優勢是mirror時的jig操作可以很方便的預先知道我們想要的對稱后的結果。但如果用代碼實現這種jig操作,我覺得有點復雜,目前我還不知道怎么實現。
我要講的主要就是用代碼來實現塊的鏡像。難點就在與文字的鏡像,和標注的鏡像。這篇文章先講文字的鏡像。文字鏡像的主要步驟分為:
1.找到鏡像前文字邊界的四個角,這四個角構成了一個矩形,我們要求得這個矩形的長和寬所代表的向量。
2.判斷文字鏡像后的方向,如果是偏向朝Y軸鏡像,那么文字鏡像后的方向是沿着X軸翻轉的,如果是偏向朝X軸鏡像,那么文字鏡像后的方向是沿着X軸翻轉的。這里我以沿着Y軸鏡像為例子。
3.移動鏡像后切被翻轉后的文字,這里也是根據鏡像軸的不同,需按不同的向量來移動。
詳細情況見圖:

圖中左邊是要鏡像的文字,文字上的藍色線,和黃色線是我調試的時候加入的,黃線左端是 pt1,右端是pt2,藍線左端是pt3,右端是pt4。 中間的豎線是Y軸鏡像線,右邊就是不同情況下鏡像后的文字。其中黃色部分表示正確的鏡像結果,紅色部分表示:鏡像后延第一個步驟移動后求得的向量移動了文字的position但是沒翻轉的結果。黑色部分表示:鏡像后翻轉了文字但文字的position沒有按向量移動的結果。
下面我就來仔細分析一下代碼:
要實現第一步驟,前提是要有一段P/Invoke的代碼:

其中 引入的acdb22.dll是 autocad2018中的版本,不同版本,這個dll后面的數字不一樣。我們可以到cad安裝目錄下查找acdb幾個字,找到后面帶數字的就是了,64位的安裝目錄默認位置:C:\Program Files\Autodesk\AutoCAD 2018。這兩個函數一個是32位,一個是64位,具體用哪個后面的代碼會自動判斷。這個函數作用我覺得主要是求 這個name。


這里用到了accore.dll,有的cad版本沒有這個dll,就用acad.exe代替就可以了。上面的acdbEntGet主要是根據entity的名字求的entity實體的Intptr,下面的函數時求的文字邊界對角點,這里注意,我把這個兩個點用直線打印在cad空間里,發現它時在原點,沒旋轉的線,但其實文字不的position不在原點,也帶有旋轉角度。后面要求的文字邊界向量就是根據這兩個點來的。

上面求得的pt1,pt2 經過:
pt1 = pt1.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
pt2 = pt2.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
這種操作就得到了第一幅圖中的黃線。


在經過這樣的操作,得到的pt3 和pt4就是第一幅圖的藍線。這其中的rotDir和linDir就是我們要求得的寬和長代表的向量了,然后在把它給鏡像了得到的mirRotDir和mirLinDir就是鏡像后的文字要移動的向量了,這里第一步就結束了。
第二步,第三步:


大的話,就說明文字需要朝X軸翻轉,所以這里的IsMirroredInX=true就代表需要朝X軸翻轉。
緊接着下面句,如果沒加mirLineDir這個向量,就會出現第一幅圖中的畫黑線的情況,如果不加IsMirrorInX就會出現畫紅線的情況。
到這里就全部結束了。
下面給出所有代碼:
public class MyMirror { Document Doc = Application.DocumentManager.MdiActiveDocument; Editor Ed = Application.DocumentManager.MdiActiveDocument.Editor; Database Db = Application.DocumentManager.MdiActiveDocument.Database; List<Entity> list = new List<Entity>(); List<ObjectId> listOId = new List<ObjectId>(); [CommandMethod("testM")] public void MirrorTextCmd() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; //Entity selection PromptEntityOptions peo = new PromptEntityOptions( "\nSelect a text entity:"); peo.SetRejectMessage("\nMust be text entity..."); peo.AddAllowedClass(typeof(DBText), true); PromptEntityResult perText = ed.GetEntity(peo); if (perText.Status != PromptStatus.OK) return; peo = new PromptEntityOptions("\nSelect a mirror line:"); peo.SetRejectMessage("\nMust be a line entity..."); peo.AddAllowedClass(typeof(Line), true); PromptEntityResult perLine = ed.GetEntity(peo); if (perLine.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { Line line = tr.GetObject(perLine.ObjectId, OpenMode.ForRead) as Line; Line3d mirrorLine = new Line3d( line.StartPoint, line.EndPoint); MirrorText(perText.ObjectId, mirrorLine); tr.Commit(); } } void MirrorText(ObjectId oId, Line3d mirrorLine) { Database db = oId.Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { // Get text entity DBText dbText = tr.GetObject(oId, OpenMode.ForRead) as DBText; // Clone original entity DBText mirroredTxt = dbText.Clone() as DBText; // Create a mirror matrix Matrix3d mirrorMatrix = Matrix3d.Mirroring(mirrorLine); // Do a geometric mirror on the cloned text mirroredTxt.TransformBy(mirrorMatrix); // Get text bounding box Point3d pt1, pt2, pt3, pt4; GetTextBoxCorners( dbText, out pt1, out pt2, out pt3, out pt4); // Get the perpendicular direction to the original text Vector3d rotDir = pt4.Subtract(pt1.GetAsVector()).GetAsVector(); // Get the colinear direction to the original text Vector3d linDir = pt3.Subtract(pt1.GetAsVector()).GetAsVector(); // Compute mirrored directions Vector3d mirRotDir = rotDir.TransformBy(mirrorMatrix); Vector3d mirLinDir = linDir.TransformBy(mirrorMatrix); //Check if we need to mirror in Y or in X if (Math.Abs(mirrorLine.Direction.Y) > Math.Abs(mirrorLine.Direction.X)) { // Handle the case where text is mirrored twice // instead of doing "oMirroredTxt.IsMirroredInX = true" mirroredTxt.IsMirroredInX = !mirroredTxt.IsMirroredInX; mirroredTxt.Position = mirroredTxt.Position + mirLinDir; } else { mirroredTxt.IsMirroredInY = !mirroredTxt.IsMirroredInY; mirroredTxt.Position = mirroredTxt.Position + mirRotDir; } // Add mirrored text to database //btr.AppendEntity(mirroredTxt); //tr.AddNewlyCreatedDBObject(mirroredTxt, true); //list.Add(mirroredTxt); mirroredTxt.ToSpace(); tr.Commit(); } } #region p/Invoke public struct ads_name { public IntPtr a; public IntPtr b; }; // Exported function names valid only for R19 [DllImport("acdb22.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AAY01JVAcDbObjectId@@@Z")] public static extern int acdbGetAdsName32( ref ads_name name, ObjectId objId); [DllImport("acdb22.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?acdbGetAdsName@@YA?AW4ErrorStatus@Acad@@AEAY01_JVAcDbObjectId@@@Z")] public static extern int acdbGetAdsName64( ref ads_name name, ObjectId objId); public static int acdbGetAdsName(ref ads_name name, ObjectId objId) { if (Marshal.SizeOf(IntPtr.Zero) > 4) return acdbGetAdsName64(ref name, objId); return acdbGetAdsName32(ref name, objId); } [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acdbEntGet")] public static extern System.IntPtr acdbEntGet( ref ads_name ename); [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedTextBox")] public static extern System.IntPtr acedTextBox( IntPtr rb, double[] point1, double[] point2); void GetTextBoxCorners(DBText dbText, out Point3d pt1, out Point3d pt2, out Point3d pt3, out Point3d pt4) { ads_name name = new ads_name(); int result = acdbGetAdsName( ref name, dbText.ObjectId); ResultBuffer rb = new ResultBuffer(); Interop.AttachUnmanagedObject( rb, acdbEntGet(ref name), true); double[] point1 = new double[3]; double[] point2 = new double[3]; // Call imported arx function acedTextBox(rb.UnmanagedObject, point1, point2); pt1 = new Point3d(point1); pt2 = new Point3d(point2); var ptX = pt1 + Vector3d.XAxis * 40; var ptY = pt2 + Vector3d.YAxis * 50; var lX = new Line(pt1, ptX); var lY = new Line(pt2, ptY); lX.Color= Color.FromColor(System.Drawing.Color.Green); lY.Color= Color.FromColor(System.Drawing.Color.Orange); Line line = new Line(pt1, pt2); line.Color = Color.FromColor(System.Drawing.Color.Red); line.ToSpace(); lX.ToSpace(); lY.ToSpace(); // Create rotation matrix Matrix3d rotMat = Matrix3d.Rotation( dbText.Rotation, dbText.Normal, pt1); // The returned points from acedTextBox need // to be transformed as follow pt1 = pt1.TransformBy(rotMat).Add(dbText.Position.GetAsVector()); pt2 = pt2.TransformBy(rotMat).Add(dbText.Position.GetAsVector()); Line linetrans = new Line(pt1, pt2); linetrans.Color = Color.FromColor(System.Drawing.Color.Yellow) ; linetrans.ToSpace(); Vector3d rotDir = new Vector3d( -Math.Sin(dbText.Rotation), Math.Cos(dbText.Rotation), 0); //求垂直於rotDir和normal的法向量 Vector3d linDir = rotDir.CrossProduct(dbText.Normal); double actualWidth = Math.Abs((pt2.GetAsVector() - pt1.GetAsVector()) .DotProduct(linDir)); pt3 = pt1.Add(linDir * actualWidth); pt4 = pt2.Subtract(linDir * actualWidth); Line linetrans2 = new Line(pt3, pt4); linetrans2.Color = Color.FromColor(System.Drawing.Color.Blue); linetrans2.ToSpace(); } #endregion }