cad.net 块编辑器&在位编辑器

块编辑器bedit

1,使用期间若是用代码克隆另一个块表记录进来,并保存,不会刷新.
2,通过打开块编辑器之后,直接保存,不会刷新.
可能这是块编辑器内置了一个flag,看某个图元是不是有改过?

普通块:块刷新函数就可以了.
动态块:图元move0距离才可以刷新.

在位编辑器refedit

原理

在位编辑本质是一个长事务它不是mysql的长时间事务的意思.
长事务中有个WorkSet映射表,
把块表记录的图元克隆一份到当前空间,然后set加入图元,
它是在位编辑块编辑器的基础.
当编辑器保存时候,会将全部数据回写映射对象中.

1:要保证块表记录存在吗?
答:由于自带的组块命令不能在位编辑时组块,我用代码实现了一个组块就可以了.
这表示了实际上没有锁块表.

2:编辑期间可以删除编辑中的块表记录吗?
假如删除掉当前编辑中的记录会怎么样?
答:我没有测试.

3:块编辑器和在位编辑是共享WorkSet的吗?
如果是岂不是块编辑器期间可以在位编辑?
答:我没有测试.

4:在位编辑的时候会产生0-RefEdit0图层,不知道何用?
猜测是不是锁了0图层之后,用在位编辑命令,就会自动生成一个图层.

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

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

判断块内外图元

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

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

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

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

自制在位编辑

构造一个映射表map或者集合set.

1,把当前空间图元设置褪色度
pEnt->setTransparency(90) 值越大越透明
2,块内图元记录并映射到当前空间(模型/布局).
3,新增图元
用户命令绘制的图元,从 图元添加事件 添加入refMap<块内图元id, bool块内>中.
4,减去
是将refMap.value标记为false,并设置一下褪色度.
5,保存
refMap[?]==false的加入到外层块表记录.
refMap[?]==true的保存入块表记录内.
6,刷新图元颜色
因为需要遍历当前空间全部图元恢复褪色度,这个步骤比较耗时.
因此我们可以制作一个没有褪色度的,就不需要恢复了.
或者把块内褪色,这样就反其道而行,刷新内容就很少.

最后,记得刷新编辑的块参照.
但是貌似不需要全图重生成啊,只需要遍历获取编辑层和上层块名进行刷新就好了啊.
而且可以用惰性更新显示,把当前界面更新,每次界面移动到某位置就更新然后移除更新过的.

代码

褪色度/透明度

系统变量TRANSPARENCYDISPLAY 是否显示褪色度.

public class TransperentcyCommands {
    [CommandMethod(nameof(CmdTransperentcy))]
    public void CmdTransperentcy() {
        using DBTrans tr = new();
        var x = EditorEx.GetNoNegtiveInt("输入透明度", 255);
        var ts = new Transparency((byte)x.Value);
        foreach (var id in tr.BlockTable) {
            if (id.IsOk()) continue;
            using var obj = tr.GetObject(id);
            if (obj is not BlockTableRecord btr) continue;
            foreach (var entid in btr) {
                using var ent = (Entity)tr.GetObject(entid, openLockedLayer: true);
                ent.Transparency = ts;
            }
        }
    }
}

.NET

cad.net 动态编译生成命令
书写上面链接的代码期间,发现了在位编辑时,选择图元并使用API修改,
是可以修改块外图元的(褪色的图元).

在位编辑器是一个长事务,不过我在之前并不知情,
用了一个通过差集过滤,速度肯定比官方长事务慢的.

方案一,长事务API

public static class DocumentEx {
    /// <summary>
    /// 判断对象是否在工作集中(判断在位编辑块时块内外图元)
    /// </summary>
    /// <param name="doc">块所在文档</param>
    /// <param name="entityId">文档中的实体Id</param>
    /// <param name="includingErased">是否包含删除的对象</param>
    /// <returns></returns>
    public static bool WorkSetHas(this Document doc, ObjectId entityId, bool includingErased = false) {
        var id = Acap.LongTransactionManager.CurrentLongTransactionFor(doc);
        // 当前长事务不存在
        if (id.IsNull) return false;
        var tr = DBTrans.GetTop(doc.Database);
        using var longtr = (LongTransaction)tr.GetObject(id, openErased: includingErased);
        return longtr.WorkSetHas(entityId, includingErased);
    }
}

方案二,差集过滤

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
using System.Linq;

public static class RefeditManager
{
    private static readonly Dictionary<Document, HashSet<ObjectId>> _workSets = new();
    
    [IFoxInitialize]
    public static void Initialize()
    {
        var dm = Application.DocumentManager;
        
        // 初始化已打开的文档
        foreach (Document doc in dm)
        {
            InitializeDocument(doc);
        }
        
        dm.DocumentCreated += (sender, e) => InitializeDocument(e.Document);
        dm.DocumentToBeDestroyed += (sender, e) => _workSets.Remove(e.Document);
    }
    
    private static void InitializeDocument(Document doc)
    {
        if (!_workSets.ContainsKey(doc))
        {
            _workSets[doc] = new HashSet<ObjectId>();
            
            doc.CommandWillStart += (sender, e) => OnCommandWillStart(doc, e);
            doc.CommandEnded += (sender, e) => OnCommandEnded(doc, e);
        }
    }
    
    // 获取文档的工作集(只读)
    public static IReadOnlyCollection<ObjectId> GetWorkSet(Document doc)
    {
        return _workSets.TryGetValue(doc, out var set) ? set.ToList().AsReadOnly() : null;
    }
    
    private static void OnCommandWillStart(Document doc, CommandEventArgs e)
    {
        if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
            return;
            
        if (e.GlobalCommandName.ToUpper() == "REFEDIT")
        {
            // 开始REFEDIT时记录当前所有图元
            var filter = new SelectionFilter(new TypedValue[] { 
                new TypedValue((int)DxfCode.Start, "LINE") 
            });
            
            var result = doc.Editor.SelectAll(filter);
            if (result.Status == PromptStatus.OK)
            {
                _workSets[doc] = new HashSet<ObjectId>(result.Value.GetObjectIds());
            }
        }
    }
    
    private static void OnCommandEnded(Document doc, CommandEventArgs e)
    {
        if (string.IsNullOrEmpty(e.GlobalCommandName) || e.GlobalCommandName == "#")
            return;
            
        var command = e.GlobalCommandName.ToUpper();
        
        switch (command)
        {
            case "REFEDIT":
                // 更新工作集
                var filter = new SelectionFilter(new TypedValue[] { 
                    new TypedValue((int)DxfCode.Start, "LINE") 
                });
                
                var result = doc.Editor.SelectAll(filter);
                if (result.Status == PromptStatus.OK)
                {
                    _workSets[doc] = new HashSet<ObjectId>(result.Value.GetObjectIds());
                }
                break;
                
            case "REFSET":
                // 处理添加/删除图元
                HandleRefset(doc);
                break;
                
            case "REFCLOSE":
                _workSets[doc].Clear();
                break;
        }
    }
    
    private static void HandleRefset(Document doc)
    {
        var ed = doc.Editor;
        
        // 获取前一个选择集(REFSET命令选择的对象)
        var psr = ed.SelectPrevious();
        if (psr.Status != PromptStatus.OK)
            return;
            
        var selectedIds = psr.Value.GetObjectIds().ToHashSet();
        
        // 过滤选择集(只保留LINE图元)
        var lineIds = selectedIds.Where(id => 
        {
            using (var tr = doc.TransactionManager.StartOpenCloseTransaction())
            {
                using var entity = tr.GetObject(id, OpenMode.ForRead) as Entity;
                return entity?.GetType().Name.ToUpper() == "LINE";
            }
        }).ToHashSet();
        
        // 获取最后一行命令提示判断是添加还是删除
        string lastPrompt = Application.GetSystemVariable("LASTPROMPT") as string;
        
        if (lastPrompt.Contains("添加") || lastPrompt.Contains("Added"))
        {
            // 添加模式:将选择的图元加入工作集
            _workSets[doc].UnionWith(lineIds);
        }
        else if (lastPrompt.Contains("删除") || lastPrompt.Contains("Removed"))
        {
            // 删除模式:从工作集中移除选择的图元
            _workSets[doc].ExceptWith(lineIds);
        }
    }
}

ARX

通过长事务判断在位编辑块内外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;
  }

Lisp

0x01 koz方案

(defun C:save-refedit-handle (reactor params)
  (setenv "reflast"
          (IF (= (strcase (car params)) "REFEDIT")
            (cdr (assoc 5 (entget (entlast))))
            ""
          )
  )
)
(vlr-add (setq *refedit-reactor*
                (vlr-command-reactor
                  "RefEditReactor"
                  '((:vlr-commandWillStart . C:save-refedit-handle)
                   )
                )
         )
)

0x02 中线dcl1214

;($block-refedit$(car (entsel))nil)

(defun $block-refedit$ (b lst / ss e es obj)
  (if (and b (= (type b) 'ename) (entget b))
    (progn
      (setq ss (lst->ss (list b)))
      (and ss (sssetfirst ss ss))
      (if ss
  (progn
    (setq  e (vlax-vla-object->ename
        (setq obj (VLA-ADDPOINT
          (vla-get-ModelSpace
            (vla-get-ActiveDocument
              (vlax-get-acad-object)
            )
          )
          (VLAX-3D-POINT (LIST 0 0 0))
          )
        )
      )
    )
    (vl-cmdf "-refedit" "O" "ALL" "Y")
    (vl-catch-all-apply 'vla-put-visible
      (list
        (vl-catch-all-apply 'VLA-ITEM
          (list
            (vl-catch-all-apply 'vla-get-toolbars
              (list
                (vl-catch-all-apply 'vla-Item
                  (list (vla-get-MenuGroups (vlax-get-Acad-Object)) "ACAD" )
                )
              )
            )
            "参照编辑"
            )
          )
        0
      )
    )
     ;关闭
    (setq ss nil)
    (setq es nil)
    (while (AND e (setq e (entnext e)))
      (setq es (cons e es))
    )  ;进入refedit后调用这个
    (vla-delete obj)
  )
      )
    )
  )
  es
)

(完)

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