AutoCad 二次开发 文字镜像

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.移动镜像后切被翻转后的文字,这里也是根据镜像轴的不同,需按不同的向量来移动。
 
详细情况见图:
AutoCad 二次开发 文字镜像_第1张图片
图中左边是要镜像的文字,文字上的蓝色线,和黄色线是我调试的时候加入的,黄线左端是 pt1,右端是pt2,蓝线左端是pt3,右端是pt4。 中间的竖线是Y轴镜像线,右边就是不同情况下镜像后的文字。其中黄色部分表示正确的镜像结果,红色部分表示:镜像后延第一个步骤移动后求得的向量移动了文字的position但是没翻转的结果。黑色部分表示:镜像后翻转了文字但文字的position没有按向量移动的结果。
下面我就来仔细分析一下代码:
要实现第一步骤,前提是要有一段P/Invoke的代码:
AutoCad 二次开发 文字镜像_第2张图片
其中 引入的acdb22.dll是 autocad2018中的版本,不同版本,这个dll后面的数字不一样。我们可以到cad安装目录下查找acdb几个字,找到后面带数字的就是了,64位的安装目录默认位置:C:\Program Files\Autodesk\AutoCAD 2018。这两个函数一个是32位,一个是64位,具体用哪个后面的代码会自动判断。这个函数作用我觉得主要是求 这个name。
AutoCad 二次开发 文字镜像_第3张图片
 
AutoCad 二次开发 文字镜像_第4张图片
这里用到了accore.dll,有的cad版本没有这个dll,就用acad.exe代替就可以了。上面的acdbEntGet主要是根据entity的名字求的entity实体的Intptr,下面的函数时求的文字边界对角点,这里注意,我把这个两个点用直线打印在cad空间里,发现它时在原点,没旋转的线,但其实文字不的position不在原点,也带有旋转角度。后面要求的文字边界向量就是根据这两个点来的。
AutoCad 二次开发 文字镜像_第5张图片
上面求得的pt1,pt2 经过:
pt1 = pt1.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
pt2 = pt2.TransformBy(rotMat).Add(dbText.Position.GetAsVector());
这种操作就得到了第一幅图中的黄线。
AutoCad 二次开发 文字镜像_第6张图片
在经过这样的操作,得到的pt3 和pt4就是第一幅图的蓝线。这其中的rotDir和linDir就是我们要求得的宽和长代表的向量了,然后在把它给镜像了得到的mirRotDir和mirLinDir就是镜像后的文字要移动的向量了,这里第一步就结束了。
第二步,第三步:
AutoCad 二次开发 文字镜像_第7张图片
AutoCad 二次开发 文字镜像_第8张图片
 
大的话,就说明文字需要朝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 list = new List();
        List listOId = new List();

        [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
    }

 

你可能感兴趣的:(AutoCad 二次开发 文字镜像)