演示
左邊是旋轉的,右邊是左邊鏡像的.
用了Attsync命令之后,左邊是旋轉的,會發現文字是正的..右邊則是反的.

用了Attsync命令之后,左邊是旋轉的,會發現文字是正的..右邊則是反的.*2

動圖演示

出問題的函數
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不一樣顯示
}
}
}
(完)
