- 积分
- 10875
- 明经币
- 个
- 注册时间
- 2015-8-18
- 在线时间
- 小时
- 威望
-
- 金钱
- 个
- 贡献
-
- 激情
-
|
发表于 2024-10-16 17:24:07
|
显示全部楼层
本帖最后由 你有种再说一遍 于 2024-10-17 19:22 编辑
高版本新功能增加的问题真多,连组块和炸开都充满荆棘.
# 组块:
0,选择或创建图元/属性定义.
1,基变换:插入点到原点
2,获取绘图次序之后深度克隆,然后创建块表记录并加入其中
https://www.cnblogs.com/d1742647821/p/18325096
3,注释性
https://gitee.com/inspirefunction/ifoxcad/issues/I820Z0
4,根据属性定义附加属性参照
5,附加动态参数和动作(这里甚至没有完成)
6,基变换:原点到插入点
# 炸开(分解):
理论上炸开底层实现是克隆,可以改用克隆到当前块表记录,然后标注关联要依靠什么什么重建(或者保留key关系)才对.
没找到C#怎么重建标注关联...
注意:
新旧版本炸开不同,不能单纯认为可以相互替代,尤其是新版组块旧版打开炸开.
旧版:BlockReference.Explode();
新版:BlockReference.ExplodeToOwnerSpace()
旧版表现:
标注关联被取消,新版阵列被炸开会怎么样等等.
我们应该用插件进行反向支持,在低版本补充炸开高版本炸开功能才对.
官方保存低版本dwg有没有自动转换动态块内容呢?
新版表现:
能够保留块内的标注关联.
如果遇到新版阵列,那么需要把块参照进行逆变换到原点,否则阵列内容会跑位.
这个函数没有提供返回值,炸开内容只能通过db.ObjectAppended事件获取,而事件会将全部层次结构和过程id给平铺了.
可以通过遍历事件收集的id输出DBObject.Gettype().Name得知:
它们并不都是Entity,会有IsErased==true的,会有重复的,会有动态块参数和动作的.
而直接把全部内容进行矩阵变换,则会把图元变换了两次.
阵列原理:
https://www.cnblogs.com/ztcad/p/14707974.html
https://www.cnblogs.com/ztcad/p/14707880.html
一个罕见函数,转为静态块:
BlockReference.ConvertToStaticBlock();
测试代码:
```csharp
public class TestBlock {
[CommandMethod(nameof(cs21))]
public void cs21() {
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptEntityOptions peo = new("选择块参照: ");
var per = ed.GetEntity(peo);
if(per.Status != PromptStatus.OK) return;
db.ObjectAppended += ObjectAppended;
Matrix3d mat = Matrix3d.Identity;
using(var trans = db.TransactionManager.StartTransaction()) {
var ent = (Entity)trans.GetObject(per.ObjectId, OpenMode.ForRead);
if(ent is not BlockReference br) return;
br.UpgradeOpen();
// 记录块矩阵,给炸开后的内部图元使用
mat = br.BlockTransform;
// 把块参照逆变换到原点
br.TransformBy(mat.Inverse());
// 新版炸开
br.ExplodeToOwnerSpace();
// 删除实际上是修改IsErased=true,并通知数据库把它放入UndoLog用于撤回,并不是真正删除数据行,它仍然在内存暂留,所以仍然需要降权.
br.Erase();
br.DowngradeOpen();
trans.Commit();
}
db.ObjectAppended -= ObjectAppended;
// 目的:炸开后的图元全部变换到块矩阵
// 从事件获取内容
// 1,存在过程图元,有被删除的图元.(过程图元会恢复吗?)
// 2,动态块参数也在其中.(它是隐藏的吗?)
// 3,仅排除ent.IsErased不行的,有些东西无法矩阵变换.
// 4,如果将可以变换的都进行矩阵变换,会把阵列内容移动了两次,而仅仅移动前面十个就不会(正确变换),如何排除呢?前面十个中有AcDbAssocArrayActionBody是不是就是阵列参数,而十个之后是阵列图元?
using (DocumentLock docLock = doc.LockDocument()) {
using(var t2 = db.TransactionManager.StartTransaction()) {
// 统计重复(开窗函数),发现都是1,就不用输出了
var map = new Dictionary<ObjectId,int>();
for(int j = 0; j < _ids.Count; j++){
var id = _ids[j];
if(map.ContainsKey(id)) {
++map[id];
}else{ map.Add(id,1); }
}
// 打印统计信息
var sb = new StringBuilder();
for(int j = 0; j < _ids.Count; j++){
var id = _ids[j];
string name = "";
/* 已经删除打开会报错eWasErased
无论是id.Open,还是OpenMode.ForNotify模式
那么Acad撤回时候怎么用UndoLog复活它呢?先设置id.IsErased=true吗?还是说深度克隆?只允许内部实现?通过id的话那么图元内容无法获取.
if(id.IsErased){
var objx = t2.GetObject(id, OpenMode.ForRead);
name=objx.GetType().Name;
}
*/
if(id.IsErased) name+="-"
if(id.IsNull) name+="*"
//全有 if(id.IsValid) name+="+"
if(id.IsEffectivelyErased) name+="/"
//全有 if(id.IsResident) name+=">"
name += id.ObjectClass.Name;
sb.AppendLine($"序号:{j},句柄:{new Handle(id.Handle.Value)},重复:{map[id]},图元名称:{name}");
}
Debug.Print(sb.ToString());
// 测试前面十个
for(int j = 0; j < 10; j++){
var id = _ids[j];
if(id.IsErased) continue;
var objx = t2.GetObject(id, OpenMode.ForRead);
if(objx.GetType().Name is "BlockBegin" or "BlockEnd") continue; //跳过不能矩阵变换的对象,它将导致eNotApplicable.
if(objx is not Entity ent) continue;
ent.TransformBy(mat);
ent.DowngradeOpen();
}
t2.Commit();
}
}
// 清理,这里仅仅是重置容器计数,而不释放内存容量,下次使用将从0进行覆盖内容.
// 容器持有对象不释放,并不导致对象引用计数(菱形引用计数从不为0,无法释放对象),不需要改用WeakMap.
// 因为容器持有的id是二级指针,它指向的Entity才是真正对象,所以你甚至会发现桌子设计数据库时,Entity字段从来不持有另一个Entity而是持有id,标识符模式.
_ids.Clear();
}
// 容器记录炸开内容
List<ObjectId> _ids = new();
// 通过事件获取炸开内容,保存入容器中
void ObjectAppended(object sender, ObjectEventArgs e) {
_ids.Add(e.DBObject.ObjectId);
}
}
``` |
|