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
)
(完)
浙公网安备 33010602011771号