cad.net Attsync命令导致属性块的属性位置在高低cad版本产生不同的效果

演示

左边是旋转的,右边是左边镜像的.

用了Attsync命令之后,左边是旋转的,会发现文字是正的..右边则是反的.

img

用了Attsync命令之后,左边是旋转的,会发现文字是正的..右边则是反的.*2

img

动图演示

img

出问题的函数

at.SetAttributeFromBlock(attDef, brf.BlockTransform);//从块设置属性                                     

原因

Acad2008和Acad2019使用Attsync命令处理镜像的属性块的属性时会有不同的效果.
而桌子并不是重写一个新的命令或者写一个拓展函数,而是修改了原本的函数.
因为我用代码实现,依然会发生这样的事情.

桌子的想法也很简单:
1,考虑了法向量.
2,对齐方式是"调整",桌子还会位移了.
但是这就给我们二次开发一个很不好的地方是,我们需要自行去处理不同呈现的结果.

处理方式是写一个命令来替换掉cad自带的命令,
然后自己的命令需要对旋转或者镜像分别处理,某些情况下不调用SetAttributeFromBlock.

代码

我没有动过SetAttributeFromBlock,仅仅是注释一下其中的意义:

/// <summary>
/// 设置块属性
/// </summary>
/// <param name="at">属性引用(块外)</param>
/// <param name="blockTransform">块的矩阵变换</param> 
/// <param name="definition">属性定义(块内)</param>  
public static void SetAttributeFromBlockEx(this AttributeReference at, Matrix3d blockTransform,  AttributeDefinition definition = null)
{
    // 为了解决SetAttributeFromBlock的新旧版本不同的问题,所以这里写一个替换函数
    // 强行将cad2008的旋转方式修改成与cad2019一样.因为cad2019留意到法向量的问题
    // 原本这个函数会令TextString丢失,所以后面要重设(这里考虑不改变它
    // 旋转矩阵则不要用它,不然会设置两次旋转
    // 平移矩阵则不要用它,不然会设置两次平移

    if (definition == null) // 不提供属性定义表示不对比块内信息,就直接执行矩阵.
    {
        at.SetAttributeFromBlock(blockTransform);
    }
    else
    {
        at.SetAttributeFromBlock(definition, blockTransform);
    }
}

更为具体要看这里的操作:
ps:由于缺少的函数太多了,这里仅仅提供关键的几个操作,请不要跟我要具体的代码.

public class Command_JJAttsync
{
    //如果属性块镜像后更新,会导致属性翻转,这个功能用来翻转回来 
    [CommandMethod("JJ_Attsync", CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
    public static void JJ_Attsync()
    {
        try
        {
            Database db = HostApplicationServices.WorkingDatabase;//当前的数据库
            Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;
            ed.WriteMessage(Environment.NewLine + "****惊惊连盒-翻转属性块更新");
            var ssIdArray = Command_jjak.AllSelectByBlockName(ed, db, BlockTool.EnumAttIdentical.AllBlockName);
            if (ssIdArray.Length == 0)
            {
                return;
            }

            //遍历块内所有的属性引用,如果缺失属性引用的话,就新建属性引用;
            //如果不缺失,就将属性引用修改信息(矩阵)为属性引用上,保留文字
            Acap.DocumentManager.MdiActiveDocument.Action(() =>
            {
                var tags = new List<string>();//用来删除没有tag的属性引用.
                db.Action(tr =>
                {
                    foreach (var item in ssIdArray)
                    {
                        var ent = item.ToEntity(tr);
                        if (ent is BlockReference brf)
                        {
                            //储存属性引用
                            var attrlst = brf.ToAttributes(tr);

                            //遍历块内
                            var btRec = tr.GetObject(brf.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                            foreach (ObjectId id in btRec)
                            {
                                btRec.TraversalAttribute(tr, (attDef) =>
                                {
                                    tags.Add(attDef.Tag);
                                    bool isNewAtt = false;
                                    AttributeReference at = null;
                                    string textString = "";
                                    foreach (var ata in attrlst)
                                    {
                                        //设置 标记Tag 提示Prompt 默认TextString
                                        if (ata.Tag == attDef.Tag)
                                        {
                                            at = ata;
                                            textString = at.TextString;
                                            at.UpgradeOpen();
                                            break;
                                        }
                                    }
                                    if (at == null)
                                    {
                                        isNewAtt = true;
                                        at = new AttributeReference
                                        {
                                            Tag = attDef.Tag,
#if AC2008 || AC2009
                                            TextStyle = attDef.TextStyle,
#else
                                            TextStyleId = attDef.TextStyleId,
#endif
                                        };
                                        if (textString == "")
                                        {
                                            textString = attDef.TextString;
                                        }
                                    }

                                    //从属性定义获取属性对象的对象特性
                                    at.SetAttributeFromBlockEx(brf.BlockTransform, attDef);
                                    at.AdjustAlignment(brf.Database);//调整对齐

                                    //设置 标记Tag 提示Prompt 默认TextString 
                                    at.TextString = textString;
                                    if (isNewAtt)
                                    {
                                        //向块参照添加属性对象
                                        brf.UpgradeOpen();
                                        brf.AttributeCollection.AppendAttribute(at);
                                        brf.DowngradeOpen();
                                        tr.AddNewlyCreatedDBObject(at, true);
                                    }
                                });

                                foreach (ObjectId attId in brf.AttributeCollection)
                                {
                                    if (attId.IsOk())
                                    {
                                        var ata = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
                                        //设置 标记Tag 提示Prompt 默认TextString 
                                        if (!tags.Contains(ata.Tag))
                                        {
                                            ata.UpgradeOpen();
                                            ata.Erase(); //删除没有属性的(和as一样)
                                            ata.Dispose();
                                        }
                                    }
                                }
                            }
                            tags.Clear();
                        }
                    }
                });

                db.Action(tr =>
                {
                    foreach (var item in ssIdArray)
                    {
                        var ent = item.ToEntity(tr);
                        if (ent is BlockReference brf) //旋转到当前鼠标坐标系 CoordinateSystem3d
                        {
                            //储存属性引用
                            var attrlst = brf.ToAttributes(tr);

                            //症结点在于整体缩放时候无法通过X<1来判断块的镜像
                            //缩放或者变形
                            if (brf.ScaleFactors.X >= 0 && brf.Normal.Z == 1) //没有镜像的图元,法向量正常=> 
                            {
                                //旋转
                                foreach (var at in attrlst)
                                {
                                    at.UpgradeOpen();
                                    RotateAttributeReference(brf, at);
                                    at.DowngradeOpen();
                                    at.Dispose();
                                }
                            }
                            else if (brf.ScaleFactors.X >= 0 && brf.Normal.Z == -1)//没有镜像的图元,法向量不正常=>
                            {
                                //镜像并旋转 
                                foreach (var at in attrlst)
                                {
                                    at.UpgradeOpen();
                                    var ro = at.Rotation;
                                    MirrorAttributeReference(brf, at, ro);
                                    at.DowngradeOpen();
                                    at.Dispose();
                                }
                            }
                            else if (brf.ScaleFactors.X < 0 && brf.Normal.Z == 1)//镜像的图元,法向量正常=>
                            {
                                foreach (var at in attrlst)
                                {
                                    at.UpgradeOpen();
#if AC2008
                                    //镜像并旋转
                                    var ro = at.Rotation;
                                    MirrorAttributeReference(brf, at, -ro);
#else
                                    //测试是AC2019
                                    Alignment2019Mr1(brf, at);
#endif
                                    at.DowngradeOpen();
                                    at.Dispose();
                                }
                            }
                            else if (brf.ScaleFactors.X < 0 && brf.Normal.Z == -1) //镜像的图元,法向量不正常=>
                            {
                                foreach (var at in attrlst)
                                {
                                    at.UpgradeOpen();
#if AC2008
                                    //旋转
                                    RotateAttributeReference(brf, at);
#else
                                    //测试是AC2019
                                    Alignment2019Mr2(brf, at);
#endif
                                    at.DowngradeOpen();
                                    at.Dispose();
                                }
                            }
                        }
                    }
                });

            });

            ed.SetImpliedSelection(new ObjectId[] { }); //选择
        }
        catch
        { }
    }

    /// <summary>
    /// 处理2019cad的属性块2
    /// </summary>
    /// <param name="brf">块参照</param>
    /// <param name="at">属性</param> 
    private static void Alignment2019Mr2(BlockReference brf, AttributeReference at)
    {
        var ro = at.Rotation;

        if (at.Justify == EntityAdd.TxtAlignment("铺满") ||
            at.Justify == EntityAdd.TxtAlignment("调整"))
        {
            at.Rotation = 0;//这个角度是不会设置到图元中的,但是之后求包围盒都是0角度的了,很有趣
            var boxEdge = new GeometricExtents(at).Edge3;//求包围盒

            boxEdge.RotateBy(ro, at.Normal, at.Position);
            var ro_RightUpper = boxEdge.RightUpper;//右上
            var ro_RightDown = boxEdge.RightDown;//右下 
            var ro_MidstUpper = boxEdge.MidstUpper;//中上
            var ro_MidstDown = boxEdge.MidstDown; //中下
            var ro_Midst = boxEdge.Midst;//中间

            //先平移,不然用上下镜像会有误差的
            at.EntityMove(ro_RightUpper, ro_RightDown);

            //上下镜像  
            var mt = at.EntityMirror(ro_MidstUpper, ro_MidstDown, out _);
            at.SetAttributeFromBlockEx(mt);

            if (!(brf.Rotation > Math.PI / 2 && brf.Rotation < Math.PI / 2 * 3))
            {
                ro_Midst += ro_RightUpper.GetVectorTo(ro_RightDown);
                at.EntityRotate(Math.PI, at.Normal, ro_Midst);
            }
        }
        else
        {
            MirrorAttributeReference(brf, at, ro);

            //保证右边的旋转是对的.这里是镜像过,所以要判断brf.Rotation>=的
            if (brf.Rotation >= Math.PI / 2 && brf.Rotation < Math.PI / 2 * 3)
            {
                //求包围盒
                var boxEdge = new GeometricExtents(at).Edge3;
                {
                    at.EntityRotate(Math.PI, at.Normal, boxEdge.Midst);
                }
            }
        }
        at.AdjustAlignment(brf.Database);//调整对齐
    }

    /// <summary>
    /// 处理2019cad的属性块1
    /// </summary>
    /// <param name="brf">块参照</param>
    /// <param name="at">属性</param> 
    private static void Alignment2019Mr1(BlockReference brf, AttributeReference at)
    {
        if (at.Justify == EntityAdd.TxtAlignment("铺满") ||
            at.Justify == EntityAdd.TxtAlignment("调整"))
        {
            //求包围盒          
            var boxEdge = new GeometricExtents(at).Edge3;
            if (brf.Rotation > Math.PI / 2 && brf.Rotation < Math.PI / 2 * 3)
            {
                //平移
                var ro = at.Rotation;
                at.Rotation = 0;//这个角度是不会设置到图元中的,但是之后求包围盒都是0角度的了,很有趣
                boxEdge = new GeometricExtents(at).Edge3;//求包围盒  
                var leftUpper = boxEdge.LeftUpper.RotateBy(ro, at.Normal, at.Position);
                at.EntityMove(leftUpper, at.Position);
            }
            else
            {
                //旋转
                at.EntityRotate(Math.PI, at.Normal, boxEdge.Midst);
                at.AdjustAlignment(brf.Database);//调整对齐

                //平移
                var ro = at.Rotation;
                at.Rotation = 0;//这个角度是不会设置到图元中的,但是之后求包围盒都是0角度的了,很有趣
                boxEdge = new GeometricExtents(at).Edge3;//求包围盒  
                var leftUpper = boxEdge.LeftUpper.RotateBy(ro, at.Normal, at.Position);
                at.EntityMove(at.Position, leftUpper);
            }
        }
        else
        {
            if (brf.Rotation == Math.PI / 2)
            {
                //求包围盒
                var boxEdge = new GeometricExtents(at).Edge3;
                {
                    at.EntityRotate(Math.PI, at.Normal, boxEdge.Midst);
                    //不要at.AttributeFromBlock(mt)的,否则会set两次角度...很奇怪耶 
                    at.AdjustAlignment(brf.Database);//调整对齐 不然08和19不一样显示 
                }
            }
        }
    }

    /// <summary>
    /// 镜像块的属性引用
    /// </summary>
    /// <param name="brf">块参照</param>
    /// <param name="at">属性</param>
    /// <param name="rotation">旋转角度,因为法向量不同而选择块的正值或负值</param>
    private static void MirrorAttributeReference(BlockReference brf, AttributeReference at, double rotation)
    {
        //克隆一份属性来旋转,计算它的最小包围盒
        var atClone = at.Clone() as AttributeReference;
        atClone.Rotation = 0;
        var boxEdge = new GeometricExtents(atClone).Edge3;//求包围盒  
        //将包围盒上下两个点逆旋转到文字的位置,提供给镜像使用.
        var muPt = boxEdge.MidstUpper.RotateBy(rotation, brf.BlockTransform.CoordinateSystem3d.Zaxis, atClone.Position);
        var mdPt = boxEdge.MidstDown.RotateBy(rotation, brf.BlockTransform.CoordinateSystem3d.Zaxis, atClone.Position);
        atClone.Dispose();

        //镜像矩阵
        var mt = at.EntityMirror(muPt, mdPt, out _);
        at.SetAttributeFromBlockEx(mt);
        RotateAttributeReference(brf, at);
    }

    /// <summary>
    /// 旋转块的属性引用
    /// </summary>
    /// <param name="brf"></param>
    /// <param name="at"></param>
    private static void RotateAttributeReference(BlockReference brf, AttributeReference at)
    {
        //模仿标注,0~90度不改,270~360度不改
        //90~270度就改成与块的旋转的+180度->所以只需要处理这种情况  
        //一般的块是从x轴上面开始计算0度的,逆时针    
        if (brf.Rotation > Math.PI / 2 && brf.Rotation < Math.PI / 2 * 3)
        {
            //求包围盒
            var boxEdge = new GeometricExtents(at).Edge3;

            //at.EntityRotate(Math.PI, brf.BlockTransform.CoordinateSystem3d.Zaxis, boxEdge.Midst);
            at.EntityRotate(Math.PI, at.Normal, boxEdge.Midst);
            //不要at.AttributeFromBlock(mt)的,否则会set两次角度...很奇怪耶 
            at.AdjustAlignment(brf.Database);//调整对齐 不然08和19不一样显示
        }
    }
}

(完)

原文地址:https://www.cnblogs.com/JJBox/p/12433735.html