AutoCad 二次開發 文字映象
阿新 • • 發佈:2019-11-24
AutoCad 二次開發 文字映象
參考:https://adndevblog.typepad.com/autocad/2013/10/mirroring-a-dbtext-entity.html 在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 }