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不一樣顯示
        }
    }
}

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM