cad.net 编辑器和在位编辑器原理

块编辑器

块编辑器,不是在位编辑,
有个现象是,
用代码克隆一个块表记录进来,
按道理来说是会更新的,
这在普通块上面只需要刷新一下就可以了.
但是在动态块上面就不一样了.

我通过块编辑器移动某个图元0距离就可以更新,
而不能打开块编辑器之后直接保存,
可能这是块编辑器内置了一个flag,看某个图元是不是有改过.

在位编辑器

疑问

1, 在位编辑的时候会产生0-RefEdit0图层,不知道何用?

2,如何将选择的图元加减到在位编辑器内外.
答:命令或者长事务API.

3,在位编辑时候,克隆图元加入当前空间会被加入在块内,这是怎么产生的?
答:这是由于克隆的图元id会被长事务记录.

4,褪色度来控制?
不过偶尔cad会发生褪色度错误的情况.

原理

在位编辑的本质是一个长事务,它不是mysql的长时间事务的意思.
长事务启动时,
会映射一份源图元编辑图元的关系表,也就是DictionaryHashmap.
这个操作非常重要,
它是在位编辑块编辑器的基础.
编辑器保存时候,将全部数据回写映射对象.

判断块内外图元

在位编辑状态时,判断块内外图元.
这个实际上困扰了我很久(大概两年),直到koz帮忙了.

在位编辑命令触发前: 选择当前空间的图元id;
在位编辑命令触发后: 选择当前空间的图元id;
触发后的图元必然比触发前多,然后差集运算得出多余的就是块内的.
选择的函数就是editor.SelectAll(),使用命令事件来操作就可以了.

你必须要知道的是,
命令事件的操作要注意锁文档,防止致命错误,
但同时要防止你再次调用了命令,而它内锁了文档,你再锁就会出错.

以上操作虽然很精彩,但是有长事务的API调用,才是正规操作.

代码

cad.net 动态编译生成命令
书写上面链接的代码期间,
发现了在位编辑时,选择图元并使用API修改,
是可以修改块外图元的(褪色的图元).
在位编辑器是一个长事务,所以官方方法是长事务修改.
不过我在之前并不知情,所以用了一个通过差集过滤,速度肯定比官方慢的.

差集过滤

var dm = Application.DocumentManager;
var md = dm.MdiActiveDocument;

// 命令反应器命令前触发(全局)
dm.DocumentLockModeChanged += Dc_VetoCommand; 
// 命令反应器命令后触发
md.CommandEnded += HatchEvent.Md_CommandEnded;

// 利用命令反应器 Refedit 启动之前创建选择集
private static HashSet<ObjectId> _refeditSS_Before = [];
// 利用命令反应器 Refedit 启动之后做差集,就是在位编辑的时候内部的图元
private static HashSet<ObjectId> _refeditSS_Interior = [];

两个反应器写法

/// <summary>
/// 反应器->命令否决触发命令前(不可锁文档)
/// </summary>       
public static void Dc_VetoCommand(object sender, DocumentLockModeChangedEventArgs e)
{
    //这里不可以声明database...不然关闭文档的时候,再打开会引发致命错误
    if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
        return;

    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    switch (e.GlobalCommandName.ToUpper())
    {
        case "REFEDIT":
            {
                //在位编辑命令,使用前获取当前空间所有图元
                var prompt = ed.SelectAll(_filter);//全选 
                if (prompt.Status != PromptStatus.OK)
                    return;
               var _hatchIds = prompt.Value.GetObjectIds();
                for (int i = 0; i < _hatchIds.Count; i++)
                    _refeditSS_Before.Add(_hatchIds[i]);
            }
            break;
    }
 }

/// <summary>
/// 反应器->command命令完成后(内锁文档)
/// </summary>
public static void Md_CommandEnded(object sender, CommandEventArgs e)
{
     if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
        return;

    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    switch (e.GlobalCommandName.ToUpper())
    {
        case "REFEDIT":
            {
                //在位编辑命令,使用后获取当前空间所有图元 
                var prompt = ed.SelectAll(_filter);//全选 
                if (prompt.Status != PromptStatus.OK)
                    return;
                
                var _hatchIds = prompt.Value.GetObjectIds();
                for (int i = 0; i < _hatchIds.Count; i++)
                    if (!_refeditSS_Before.Contains(_hatchIds[i]))//Except
                        _refeditSS_Interior.Add(_hatchIds[i]);

                var sb = new StringBuilder();
                foreach (var id in _refeditSS_Interior)
                    sb.AppendLine(id.ToString());
                Env.Printl("块内填充id:" + sb.ToString());
            }
            break;
        case "REFSET": //加减在位编辑图元
            {
                //完成后必然有上次选择集
                var psr = ed.SelectPrevious();
                if (psr.Status != PromptStatus.OK)
                    return;

               // 上次选择集无法利用过滤器,因此我们手动过滤.你也可以封装一个方法.
               // 在位编辑时候肯定是当前数据库,所以你不需要像我这样复杂的获取
               // 如果你不需要过滤填充,也不需要写这段
                var _hatchIds = new HashSet<ObjectId>();
                using (Transaction tr = ids[0].Database.TransactionManager.StartTransaction())
                {
                    foreach (var id in psr.Value.GetObjectIds())
                    {
                        if (id.ToEntity(tr) is Hatch ha)
                            _hatchIds.Add(ha);
                    }
                }
                if (_hatchIds.Count == 0) return;
                string last = CadSystem.Getvar("lastprompt"); //再获取最后一行命令

                // 就是因为无法遍历到在位编辑的块内图元,只能进行布尔运算
                if (last.Contains("添加") || last.Contains("Added"))// 中英文cad
                {
                    for (int i = 0; i < _hatchIds.Count; i++)
                    {
                        _refeditSS_Before.Remove(_hatchIds[i]);
                        _refeditSS_Interior.Add(_hatchIds[i]);
                    }

                    return;
                }
                if (last.Contains("删除") || last.Contains("Removed"))// 中英文cad
                {
                    for (int i = 0; i < _hatchIds.Count; i++)
                    {
                       _refeditSS_Interior.Remove(_hatchIds[i]);
                        _refeditSS_Before.Add(_hatchIds[i]);
                    }
                    return;
                }
            }
            break;
        case "REFCLOSE"://保存块,清空
            _refeditSS_Interior.Clear(); 
            break;
    }
}

长事务API处理

通过长事务判断在位编辑块内外cpp,看e大介绍是acad08就有这个API了
http://bbs.mjtd.com/thread-190822-1-1.html

    ads_name ent;
    ads_point pt;
    if(RTNORM != acedEntSel(_T("\n选择对象: "),ent,pt)){
      return;
    }
    AcDbObjectId  objId;
    acdbGetObjectId(objId,ent);
    //直接判断
    //if(acdbIsInLongTransaction(objId))
    //判断并移除工作集
    if(isWorksetAndRemove(objId))
      acutPrintf(_T("\n在长事务中"));
    else
      acutPrintf(_T("\n不在长事务中"));
  
  // 从工作集中移除
  static bool isWorksetAndRemove(AcDbObjectId objId){
    AcDbObjectId longtransId = acapLongTransactionManagerPtr()->currentLongTransactionFor(curDoc());
    if (AcDbObjectId::kNull != longtransId)
    {
      AcDbObjectPointer<AcDbLongTransaction> pLongTrans(longtransId,AcDb::kForRead);
      if(Acad::eOk != pLongTrans.openStatus()) return false;
      //判断是否在工作集

      if(pLongTrans->workSetHas(objId))
      {
        //升级打开
        pLongTrans->upgradeOpen();
        if(pLongTrans->isWriteEnabled())
        {
          //移除工作集
          pLongTrans->removeFromWorkSet(objId);
        }
        return true;
      }      
    }
    return false;
  }

(完)

posted @ 2019-07-14 18:51  惊惊  阅读(1026)  评论(0编辑  收藏  举报