Конвертация блоков в Point3d объекты

Задача
Пользователям, работающим в "голом" AutoCAD, присылают dwg-файлы, содержащие топосъёмку в виде вхождений блоков, расположенных в одной плоскости и содержащих в своём определении атрибут с координатой Z. Нужно все вхождения блоков заменить объектами Point3d, взяв координаты X и Y из точки вставки блока, а координату Z - из определённого атрибута вхождения блока. Причём в составе файла содержится не одно определение блока, инкапсулирующего в себе информацию о точке, а два.
На скрине показан маленький фрагмент трассы (для наглядности):


Пример такого файла здесь.

Решение
Не факт, что в следующем файле точки будут определяться так же двумя видами блоков с теми же именами атрибутов, поэтому данную информацию лучше вынести во внешний конфигурационный файл, хранящийся в том же каталоге, в котором лежит сама библиотека и который, при необходимости, пользователь сможет отредактировать вручную. Данный файл должен хранить в себе имена блоков, инкапсулирующих информацию о точке.

Содержимое файла настроек (Settings.xml):

  1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <!--Разработчик плагина: Андрей Бушман-->
   3:  <!--Имена блоков, вхождения которых следует выбрать-->
   4:  <Blocks>
   5:    <!--Name - имя блока; AttributeZ - имя атрибута, содержащего в себе координату Z точки.-->
   6:    <Block Name ="SPOT" AttributeZ="SPOT_ELEV"/>
   7:    <Block Name ="SPOT_01" AttributeZ="SPOT_ELEV"/>
   8:  </Blocks>

Программный код на C# (написан на .Net Framework 3.5 SP1):

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  //Acad
   5:  using acad = Autodesk.AutoCAD.ApplicationServices.Application;
   6:  using Autodesk.AutoCAD.ApplicationServices;
   7:  using Autodesk.AutoCAD.DatabaseServices;
   8:  using Autodesk.AutoCAD.EditorInput;
   9:  using Autodesk.AutoCAD.Runtime;
  10:  using System.Xml.Linq;
  11:  using System.Linq;
  12:  using System.IO;
  13:  using Autodesk.AutoCAD.Geometry;
  14:   
  15:  namespace PointsFromBlocks
  16:  {
  17:      public class Class1
  18:      {
  19:          const string ns = "hwd";
  20:          Document dwg;
  21:          Database db;
  22:          Editor ed;
  23:          string xmlFileName;
  24:   
  25:          public Class1()
  26:          {
  27:              Initialize();
  28:              xmlFileName = "Settings.xml";
  29:              acad.DocumentManager.DocumentActivationChanged += new DocumentActivationChangedEventHandler(DocumentManager_DocumentActivationChanged);
  30:          }
  31:   
  32:          void DocumentManager_DocumentActivationChanged(object sender, DocumentActivationChangedEventArgs e)
  33:          {
  34:              Initialize();
  35:          }
  36:          /// <summary>
  37:          /// При переключении на другой документ чертежа, произвожу переинициализацию переменных
  38:          /// </summary>
  39:          private void Initialize()
  40:          {
  41:              dwg = acad.DocumentManager.MdiActiveDocument;
  42:              db = dwg.Database;
  43:              ed = dwg.Editor;
  44:              ed.WriteMessage(string.Format("\nТекущим выбран документ '{0}'.\n", dwg.Name));
  45:          }
  46:   
  47:          /// <summary>
  48:          /// Построение объектов Point3d на основе имеющихся вхождений определённых блоков, с извлечением координаты Z из атрибута
  49:          /// вхождения, с последующим удалением вхождений-источников. Имена блоков, и атрибутов, хранящих координату Z хранятся в 
  50:          /// файле настроек Settings.xml.
  51:          /// </summary>
  52:          [CommandMethod(ns, "www", CommandFlags.Modal)]
  53:          public void Start()
  54:          {
  55:              //Получаю полный путь к xml-файлу настроек и инкапсулирую его содержимое в объекте XElement
  56:              string xmlFullFileName = Path.Combine((new FileInfo(this.GetType().Assembly.FullName)).DirectoryName, xmlFileName);
  57:              XElement xml = XElement.Load(xmlFullFileName);
  58:   
  59:              //Формирую запрос на извлечений имён всех блоков, вхождения которых необходимо выбрать
  60:              IEnumerable<TypedValue> blocksDef = xml.Elements().Select(n => new TypedValue((int)DxfCode.BlockName, n.Attribute("Name").Value));
  61:   
  62:              //Формирую словарь, ключом которого является имя блока, а значением - имя атрибута, хранящего координату Z.
  63:              Dictionary<string, string> dict = new Dictionary<string, string>();
  64:              foreach (TypedValue item in blocksDef)
  65:              {
  66:                  string key = item.Value.ToString();
  67:                  string value = xml.Elements().Where(n => n.Attribute("Name").Value == key).First().Attribute("AttributeZ").Value;
  68:                  dict.Add(key, value);
  69:              }
  70:   
  71:              //Строю фильтр, согласно которому должны выбираться все вхождения блоков, чьи имена присутствуют в данном фильтре.
  72:              int i = 0;
  73:              TypedValue[] vals = new TypedValue[blocksDef.Count() + 2];
  74:              vals.SetValue(new TypedValue(-4, "<or"), i++);
  75:              foreach (TypedValue item in blocksDef)
  76:              {                
  77:                  vals.SetValue(item, i++);                
  78:              }
  79:              vals.SetValue(new TypedValue(-4, "or>"), i);
  80:              SelectionFilter filter = new SelectionFilter(vals); 
  81:             
  82:              //Запрашиваю у пользователя набор объектов, к которым следует применить фильтр...
  83:              PromptSelectionResult result = ed.GetSelection(filter);
  84:              if (result.Status == PromptStatus.OK)
  85:              {
  86:                  ed.WriteMessage(string.Format("\nВыбрано {0} объектов...\n", result.Value.Count));
  87:                  using (Transaction transaction = dwg.TransactionManager.StartTransaction())
  88:                  {
  89:                      int pointsCount = 0;//Счётчик построенных точек
  90:                      //Таблица блоков
  91:                      BlockTable blockTable = transaction.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
  92:                      //Пространство модели
  93:                      BlockTableRecord modelSpaceRecord = transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
  94:                      
  95:                      //В цикле обрабатываю все вхождения блоков, выбранных пользователем
  96:                      foreach (ObjectId id in result.Value.GetObjectIds())
  97:                      {
  98:                          BlockReference obj = transaction.GetObject(id, OpenMode.ForRead) as BlockReference;
  99:                          double z = obj.Position.Z;//По умолчанию координату Z инициализирую значением самого вхождения
 100:                          bool isZ = false;//Метка о том, найдена ли в атрибутах координата Z
 101:   
 102:                          //В цикле прохожу по атрибутом вхождения блока, в поисках атрибута с нужным мне именем
 103:                          foreach (ObjectId attId in obj.AttributeCollection)
 104:                          {
 105:                              AttributeReference attRef = transaction.GetObject(attId, OpenMode.ForRead ) as AttributeReference;
 106:                              if (attRef.Tag ==dict[obj.Name])
 107:                              {
 108:                                  z = Double.Parse(attRef.TextString.Replace(',', '.')); isZ = true; break;
 109:                              }
 110:                          }
 111:                          //Если не удалось получить из атрибута координату Z, уведомляю об этом проектировщика, точку не создаю и
 112:                          //вхождение блока не удаляю (дабы всё было наглядно).
 113:                          if (!isZ)
 114:                          {
 115:                              ed.WriteMessage(string.Format("\nПроверьте содержимое атрибута, хранящего координату Z у вхождения блока '{3}', расположенного в точке '({0},{1},{2}'",
 116:                                 obj.Position.X, obj.Position.Y, obj.Position.Z, obj.Name));
 117:                          }
 118:                          else
 119:                          {
 120:                              //Создаю новый объект Point3d и добавляю его в базу данных чертежа
 121:                              DBPoint point = new DBPoint(new Point3d(obj.Position.X, obj.Position.Y, z));
 122:                              point.SetDatabaseDefaults();
 123:                              blockTable.UpgradeOpen();
 124:                              modelSpaceRecord.AppendEntity(point);
 125:                              transaction.AddNewlyCreatedDBObject(point, true);
 126:                              ++pointsCount;
 127:   
 128:                              //Теперь удаляю вхождение блока, на основании которого создан объект Point3d
 129:                              obj.UpgradeOpen();
 130:                              obj.Erase(true);
 131:                          }
 132:                      }
 133:                      db.Pdmode = 34;
 134:                      db.Pdsize = 1;                    
 135:                      transaction.Commit();
 136:                      ed.WriteMessage(string.Format("\nСоздано {0} объектов Point3d\n", pointsCount));
 137:                  }
 138:              }
 139:          }
 140:      }
 141:  }

В результате работы кода получается такой файл.

Вид сверху (фрагмент):


Вид сбоку:


Примечание: Белые, зелёные и синие штрихи, видные на красной линии - это различные графические объекты Lines и т.п., которые так же присутствуют на чертеже.

Библиотека загружается из AutoCAD командой _NETLOAD. Команда конвертации: hwd.www или можно просто www.

Внимание: для работы данной библиотеке, на компьютере должна быть установлена бесплатная библиотека .Net Framework 3.5 Sp1. Код ориентирован на AutoCAD 2009 SP3 Enu.

Исходный код программы здесь.
Откомпилированная версия (x86) здесь.
Откомпилированная версия (x64) здесь.

Comments