cad.net Attsync命令导致属性块的属性位置在高低cad版本产生不同的效果 演示 出问题的函数 原因 代码
左边是旋转的,右边是左边镜像的.
用了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不一样显示
}
}
}
(完)