明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 529|回复: 1

请教动态块炸开问题

[复制链接]
发表于 2024-10-13 23:24:25 | 显示全部楼层 |阅读模式
本帖最后由 elepeipei 于 2024-10-17 14:20 编辑

我有一个动态块,无论新版炸开函数还是旧版都存在问题.
新版:导致阵列跑位.
旧版:丢失标注关联.

## 这个动态块是利用高版本阵列,制作过程如下:

1,画一个圆,然后用AR阵列,再组块,然后进入块编辑状态.



2,点击参数管理器,新增两个key(参数)名称是可以改的.


3,选中阵列,在特性面板中,直接输入第二步的key名.


4,保存块编辑器后,选中块参照,在特性面板中可以看到刚才设置的列数和水平距离,并能够修改值.
实现了一种外部驱动块内阵列的效果.

经过研究之后,新版炸开是可以的.
炸开之前需要逆变换到原点,否则直接炸开的话,高版本阵列会跑位.
而旧版炸开会丢失标注关联.





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
发表于 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);
  }

}
```
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-22 18:57 , Processed in 0.158881 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表