三类块操作

块在AutoCAD中扮演着重要角色,它可以把多个图形组成一个整体,起到组织图形层次和图元复用的作用。
AutoCAD中有三种类型的块:普通快、动态属性块(可根据属性值修改图形尺寸)、增强属性块(在块中添加文字动态属性),后面两种在第一种的基础上添加了个性化功能。

针对根据动态属性块的块名获取所有块参照的问题,本文提出一种创新性的方法。

本文针对这三类块封装若干二次开发接口。

普通块

  • AddBlockTableRecord:添加块定义(块参照的模板)
  • InsertBlockReference:添加块参照
  • RemoveBlockRefrences:根据块定义名删除块参照
  • GetBlockReferenceByName:根据块定义名获取所有块参照
  • GetEntitiesInBlockReference:获取块参照中的所有实体(考虑块的深度遍历)
  • GetPointInTotalSystem:获取块参照中某点(块参照的局部坐标系)的全局坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public static ObjectId AddBlockTableRecord(Document doc, string blockName, List<ObjectId> ents)
{
Database db = doc.Database;
ObjectId resId = ObjectId.Null;
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
BlockTable bt = db.BlockTableId.GetObject(OpenMode.ForWrite) as BlockTable;
if (bt.Has(blockName))
doc.RemoveBlockDefinition(blockName);
BlockTableRecord btr = new BlockTableRecord();
btr.Name = blockName;
resId = bt.Add(btr);
transaction.AddNewlyCreatedDBObject(btr, true);
ObjectIdCollection collection = new ObjectIdCollection(ents.ToArray());
IdMapping mapping = new IdMapping();
db.DeepCloneObjects(collection, resId, mapping, false);
transaction.Commit();
}
return resId;
}

public static ObjectId InsertBlockReference(Database db, string blockName, Point3d position, Scale3d scale, double rotateAngle)
{
ObjectId blockReferenceId;
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
BlockTable blockTable = db.BlockTableId.GetObject(OpenMode.ForWrite) as BlockTable;
if (!blockTable.Has(blockName)) return ObjectId.Null;
BlockTableRecord blockTableRecord = transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
BlockReference blockReference = new BlockReference(position, blockTable[blockName]);
blockReference.ScaleFactors = scale;
blockReference.Rotation = rotateAngle;
blockReferenceId = blockTableRecord.AppendEntity(blockReference);
transaction.AddNewlyCreatedDBObject(blockReference, true);
blockTable.DowngradeOpen();
transaction.Commit();
}
return blockReferenceId;
}

public static void RemoveBlockRefrences(this Document doc, string name)
{
List<BlockReference> blocks = GetBlockReferenceByName(doc, name);
ObjectId[] objectIds = new ObjectId[blocks.Count];
for (int i = 0; i < blocks.Count; i++)
{
BlockReference block = blocks[i];
objectIds[i] = block.ObjectId;
}
DwgPainter.DeleteEntities(doc.Database, objectIds);
}

public static List<BlockReference> GetBlockReferenceByName(this Document doc, string name)
{
List<BlockReference> blockReferences = new List<BlockReference>();
Database db = doc.Database;
using (Transaction transaction = doc.TransactionManager.StartTransaction())
{
BlockTable bt = transaction.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = transaction.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
foreach (ObjectId id in btr)
{
Entity entity = transaction.GetObject(id, OpenMode.ForRead) as Entity;
if (entity is BlockReference && (entity as BlockReference).Name == name)
{
blockReferences.Add(entity as BlockReference);
}
}
transaction.Commit();
}
return blockReferences;
}

public static List<Entity> GetEntitiesInBlockReference(BlockReference block)
{
if (block == null) return null;
List<Entity> entities = new List<Entity>();
Queue<BlockReference> blocks = new Queue<BlockReference>();
blocks.Enqueue(block);
while (blocks.Count > 0)
{
BlockReference blockReference = blocks.Dequeue();
DBObjectCollection objects = new DBObjectCollection();
blockReference.Explode(objects);
foreach (var obj in objects)
{
if (obj is BlockReference)
blocks.Enqueue(obj as BlockReference);
else if (obj is Line || obj is Polyline || obj is Circle || obj is Arc || obj is Polyline2d)
entities.Add(obj as Entity);
}
}
return entities;
}

public static bool GetPointInTotalSystem(BlockReference reference, Point3d pointInLocalSystem, out Point3d pointInTotalSystem)
{
pointInTotalSystem = Point3d.Origin;
if (reference == null)
return false;
Point3d insertPoint = reference.Position;
double factor = reference.ScaleFactors.X;
double angle = reference.Rotation;
double dist = Math.Sqrt(pointInLocalSystem.X * pointInLocalSystem.X + pointInLocalSystem.Y * pointInLocalSystem.Y) * factor;
double angleDefiniton = DwgPainter.Arctan(pointInLocalSystem.Y, pointInLocalSystem.X);
angle += angleDefiniton;
if (angle > Math.PI * 2)
{
angle -= Math.PI * 2;
}
pointInTotalSystem = new Point3d(insertPoint.X + dist * Math.Cos(angle), insertPoint.Y + dist * Math.Sin(angle), 0);
return true;
}

动态属性块

  • 普通快的所有方法也适用于动态属性块
  • GetDynamicBlockPropertyValue:获取某一实体的某一属性值
  • SetDynamicBlockDistance:设置某一实体某一属性的值(值类型为距离)
  • SetDynamicBlockPropertyValue:设置某一实体某一属性的值(值类型为距离)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public static Object GetDynamicBlockPropertyValue(this ObjectId id, string propertyName)
{
Database db = id.Database;
object value = null;
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
Entity entity = transaction.GetObject(id, OpenMode.ForRead) as Entity;
if (entity is BlockReference && (entity as BlockReference).IsDynamicBlock)
{
var props = (entity as BlockReference).DynamicBlockReferencePropertyCollection;
foreach (DynamicBlockReferenceProperty prop in props)
if (prop.PropertyName == propertyName)
value = prop.Value;
}
transaction.Commit();
}
return value;
}

public static void SetDynamicBlockDistance(this ObjectId id,string propertyName, double distance)
{
Database db = id.Database;
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
Entity entity = transaction.GetObject(id, OpenMode.ForWrite) as Entity;
if (entity is BlockReference && (entity as BlockReference).IsDynamicBlock)
{
var props = (entity as BlockReference).DynamicBlockReferencePropertyCollection;
foreach (DynamicBlockReferenceProperty prop in props)
{
if (!prop.ReadOnly && prop.PropertyName == propertyName
&& prop.UnitsType == DynamicBlockReferencePropertyUnitsType.Distance)
{
object val = distance;
prop.Value = val;
}
}
}
transaction.Commit();
}
}

public static void SetDynamicBlockPropertyValue(this ObjectId id, string propertyName, string value)
{
Database db = id.Database;
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
Entity entity = transaction.GetObject(id, OpenMode.ForWrite) as Entity;
if (entity is BlockReference && (entity as BlockReference).IsDynamicBlock)
{
var props = (entity as BlockReference).DynamicBlockReferencePropertyCollection;
foreach (DynamicBlockReferenceProperty prop in props)
{
if (!prop.ReadOnly && prop.PropertyName == propertyName
&& prop.UnitsType == DynamicBlockReferencePropertyUnitsType.NoUnits)
{
object val = value;
prop.Value = val;
}
}
}
transaction.Commit();
}
}

向图纸中插入动态属性块时,原块定义会“裂变”成多种块定义(这里称原块定义为“父块定义”,裂变后的为“子块定义”),每一个块定义都对应一种具体参数。

那么,问题来了:怎么通过父块定义的名称拿到子块定义的所有块参照?目前还未在网上看到该问题的解决方案,研究一番后得到如下解法。

思路:子块定义中有一个特殊的扩展数据,其中记录着父块定义的Handle,可以根据该线索实现相关功能,如下图所示。

  • 获取特定块参照的子块定义和父块定义名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/// <summary>
/// 获取该块参照的块定义名称和的父块定义名称
/// </summary>
/// <param name="id">块参照的id号</param>
/// <returns>(子块定义名,父块定义名)</returns>
public static (string, string) GetBlockNameAndParentName(this ObjectId id) {
string name = null;
string parentName = null;

Database db = id.Database;
using (Transaction transaction = db.TransactionManager.StartTransaction()) {
var blockReference = id.GetObject(OpenMode.ForWrite) as BlockReference;
if (blockReference == null)
return (null, null);

name = blockReference.Name;
var childBlockTableRecord = db.GetBlockTableRecord(name);
if (childBlockTableRecord == null)
return (null, null);

var xdata = childBlockTableRecord.ObjectId.GetXData("AcDbBlockRepBTag");
var val = from x in xdata
where x.TypeCode == 1005
select x.Value;
if (!val.Any())
return (name, null);

var parentBlockTableRecord = db.GetBlockTableRecordByHandle(val.First().ToString());
if (parentBlockTableRecord == null)
return (name, null);
parentName = parentBlockTableRecord.Name;

transaction.Commit();
}

return (name, parentName);
}

//////////////// 辅助函数 //////////////////
// 根据块定义名称获取块定义对象
public static BlockTableRecord GetBlockTableRecord(this Database db, string blockName) {
BlockTableRecord res = null;
using (Transaction transaction = db.TransactionManager.StartTransaction()) {
BlockTable bt = transaction.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
foreach (ObjectId record in bt) {
BlockTableRecord btr = transaction.GetObject(record, OpenMode.ForRead) as BlockTableRecord;
if (btr.Name == blockName) {
res = btr;
break;
}
}
transaction.Commit();
}
return res;
}

// 根据Handle值获取块定义对象
public static BlockTableRecord GetBlockTableRecordByHandle(this Database db, string handle) {
BlockTableRecord res = null;
using (Transaction transaction = db.TransactionManager.StartTransaction()) {
BlockTable bt = transaction.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
foreach (ObjectId record in bt) {
BlockTableRecord btr = transaction.GetObject(record, OpenMode.ForRead) as BlockTableRecord;
if (btr.Handle.ToString() == handle) {
res = btr;
break;
}
}
transaction.Commit();
}
return res;
}
  • 根据父块定义的名字获取所有子块定义的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/// <summary>
/// 根据父块定义的名字获取所有子块定义的名称
/// </summary>
/// <param name="db">图形数据库</param>
/// <param name="parentName">父块定义名称</param>
/// <returns>子块定义名称数组</returns>
public static List<string> GetChildrenBlockDefinitionNames(this Database db, string parentName) {
List<string> names = new List<string>();

var parentBlockTableRecord = db.GetBlockTableRecord(parentName);
if (parentBlockTableRecord == null)
return names;

string handle = parentBlockTableRecord.Handle.ToString();

using (Transaction transaction = db.TransactionManager.StartTransaction()) {
BlockTable bt = transaction.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
foreach (ObjectId record in bt) {
BlockTableRecord btr = transaction.GetObject(record, OpenMode.ForRead) as BlockTableRecord;

var xdata = btr.ObjectId.GetXData("AcDbBlockRepBTag");
if (!xdata.Any()) continue;

var val = from x in xdata
where x.TypeCode == 1005
select x.Value;
if (!val.Any()) continue;

if (val.First().ToString() == handle) {
names.Add(btr.Name);
}
}
transaction.Commit();
}
return names;
}
  • 根据父块定义的名字获取所有块参照
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
/// 根据父块定义的名字获取所有块参照
/// </summary>
/// <param name="db">图形数据库</param>
/// <param name="parentName">父块定义名称</param>
/// <returns>所有块参照的数组</returns>
public static List<ObjectId> GetAllBlockReferencesByParentName(this Database db, string parentName) {
List<ObjectId> resIds = new List<ObjectId>();
var names = db.GetChildrenBlockDefinitionNames(parentName);
Editor ed = DwgOperators.ThisEditor;
var sel = ed.SelectAll();
if (sel.Status != PromptStatus.OK)
return resIds;
var ids = sel.Value.GetObjectIds();

using(Transaction transaction = db.TransactionManager.StartTransaction()) {
foreach (var id in ids) {
var obj = id.GetObject(OpenMode.ForRead);
if(obj is BlockReference reference) {
if (names.Contains(reference.Name))
resIds.Add(id);
}
}
transaction.Commit();
}
return resIds;
}

增强属性块

  • 普通快的所有方法也适用于增强属性块
  • InsertBlockWithAttribute:插入增强属性块的同时并设置其中的文字(必须设置,不然增强属性文字会消失,后续无法修改)
  • ChangeBlockAttributes:修改增强属性块中的文字
  • ChangeBlockAttributesColor:修改块中增强文字的颜色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public static ObjectId InsertBlockWithAttribute(this Document doc, string blockName, Point3d insertPoint,string layerName, Dictionary<string, string> attributes, double rotateAngle = 0.0, double scaleFactor = 1.0, string layoutName = "")
{
Database db = doc.Database;
ObjectId backObjectId = ObjectId.Null;
using (var docLock = doc.LockDocument())
{
using (Transaction transaction = db.TransactionManager.StartTransaction())
{
BlockTable bt = transaction.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;

BlockTableRecord modelSpace = null;
ObjectId objectId = ObjectId.Null;

if (!string.IsNullOrWhiteSpace(layoutName))
{
var layoutId = db.GetLayoutIdByName(layoutName);
if (layoutId != ObjectId.Null)
objectId = layoutId;
else
return backObjectId;
}
else
objectId = bt[BlockTableRecord.ModelSpace];
modelSpace = transaction.GetObject(objectId, OpenMode.ForWrite) as BlockTableRecord;

if (bt.Has(blockName))
{
BlockTableRecord btr = transaction.GetObject(bt[blockName], OpenMode.ForRead) as BlockTableRecord;
BlockReference blockReference = new BlockReference(insertPoint, bt[blockName]);
LayerTable layerTable = transaction.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;

if (layerTable.Has(layerName))
blockReference.Layer = layerName;
else
blockReference.Layer = "0";

blockReference.Rotation = rotateAngle;
blockReference.ScaleFactors = new Scale3d(scaleFactor);

backObjectId = modelSpace.AppendEntity(blockReference);

if (btr.HasAttributeDefinitions)
{
foreach (ObjectId id in btr)
{
DBObject obj = id.GetObject(OpenMode.ForWrite);
if (obj is AttributeDefinition)
{
AttributeReference attrRef = new AttributeReference();
attrRef.SetAttributeFromBlock((AttributeDefinition)obj, blockReference.BlockTransform);
if (attributes.ContainsKey(attrRef.Tag.ToString()))
{
var value = attributes[attrRef.Tag.ToString()];
if (value == null)
value = "";
attrRef.TextString = value;
blockReference.AttributeCollection.AppendAttribute(attrRef);
transaction.AddNewlyCreatedDBObject(attrRef, true);
}
}
}
}
transaction.AddNewlyCreatedDBObject(blockReference, true);
}
transaction.Commit();
return backObjectId;
}
}
}

public static void ChangeBlockAttributes(this Document doc, BlockReference blockReference, Dictionary<string, string> attributes)
{
if (blockReference == null || attributes == null || attributes.Count == 0)
return;
var attrs = blockReference.AttributeCollection;
if (attrs.Count == 0) return;
using (Transaction transaction = doc.TransactionManager.StartTransaction())
{
foreach (var attr in attrs)
{
AttributeReference attrRef = null;
if (attr is ObjectId)
attrRef = transaction.GetObject((ObjectId)attr, OpenMode.ForWrite) as AttributeReference;
else if (attr is AttributeReference)
attrRef = attr as AttributeReference;
if (attrRef != null && attributes.ContainsKey(attrRef.Tag.ToString()))
attrRef.TextString = attributes[attrRef.Tag.ToString()];
}
transaction.Commit();
}
}

public static void ChangeBlockAttributesColor(this Document doc, BlockReference blockReference, int colorIndex)
{
if (doc == null || blockReference == null) return;
var attrs = blockReference.AttributeCollection;
if (attrs.Count == 0) return;
using (Transaction transaction = doc.TransactionManager.StartTransaction())
{
foreach (var attr in attrs)
{
AttributeReference attrRef = null;
if (attr is ObjectId)
attrRef = transaction.GetObject((ObjectId)attr, OpenMode.ForWrite) as AttributeReference;
else if (attr is AttributeReference)
attrRef = attr as AttributeReference;
if (attrRef != null)
attrRef.ColorIndex = colorIndex;
}
transaction.Commit();
}
}

评论