Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Вильдар от 11-11-2015, 16:48:40

Название: Сравнение блоков
Отправлено: Вильдар от 11-11-2015, 16:48:40
Доброго времени суток всем,

Как можно сравнить два определения блока? Одинаковая ли у них геометрия.
Мне нужно сравнивать блоки в текущем чертеже и во внешнем файле.

Пока придумался такой вариант:
Сохранить в список границы всех объектов в блоке . Потом сравнить списки.
Тестовый пример:
Код - C# [Выбрать]
  1. // Тест сравнения блока в текущем чертеже и в файле
  2.       [CommandMethod("Test")]
  3.       public void Test()
  4.       {
  5.          Document doc = Application.DocumentManager.MdiActiveDocument;
  6.          Editor ed = doc.Editor;
  7.          Database db = doc.Database;
  8.  
  9.          bool result = false;        
  10.  
  11.          using (var t = db.TransactionManager.StartTransaction())
  12.          {            
  13.             // Тестовый блок в текущем чертеже
  14.             ObjectId idBtrTest = GetTestBtr(db);
  15.             List<Extents3d> entsInfo = getEntInfoBtr(idBtrTest);
  16.  
  17.             // Тестовый блок во внешнем файле
  18.             using (var dbLib = new Database(false, true))
  19.             {
  20.                dbLib.ReadDwgFile(@"c:\test\Lib.dwg", FileShare.ReadWrite, false, "");
  21.                using (var tLib = dbLib.TransactionManager.StartTransaction())
  22.                {
  23.                   ObjectId idBtrTestLib = GetTestBtr(dbLib);
  24.                   List<Extents3d> entsInfoLib = getEntInfoBtr(idBtrTestLib);
  25.                   // Сравнение блоков                  
  26.                   result = entsInfo.SequenceEqual(entsInfoLib);
  27.                   tLib.Commit();
  28.                }
  29.             }
  30.             t.Commit();
  31.          }
  32.          ed.WriteMessage("Результат сравнения блоков - {0}", result);
  33.       }
  34.  
  35.       private List<Extents3d> getEntInfoBtr(ObjectId idBtrTest)
  36.       {
  37.          List<Extents3d> entsInfo = new List<Extents3d>();
  38.          var btr = idBtrTest.GetObject(OpenMode.ForRead) as BlockTableRecord;
  39.          foreach (ObjectId idEnt in btr)
  40.          {
  41.             var ent = idEnt.GetObject(OpenMode.ForRead) as Entity;
  42.             entsInfo.Add(ent.GeometricExtents);
  43.          }
  44.          return entsInfo;
  45.       }
  46.  
  47. private ObjectId GetTestBtr(Database db)
  48.       {
  49.          var bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
  50.          if (bt.Has("Test"))
  51.          {
  52.             return bt["Test"];
  53.          }
  54.          return ObjectId.Null;        
  55.       }

Вроде работает. Но, в этом случае большое значение имеет порядок объектов в блоке. При его изменении, но сохранении геометрии и расположения, вернет false.

Плиз, подскажите плюсы/минусы моего способа, и как его улучшить.
Или если есть другие идеи.

Спасибо!

/ Внес уточнения /
Название: Re: Сравнение блоков
Отправлено: Александр Пекшев aka Modis от 11-11-2015, 16:55:31
Не совсем конкретно поставлена задача! Какие варианты различий могут случиться? Может, например, достаточно сравнивать только GeometricExtents вхождения блока и не более
Да к тому-же - я могу изменить масштаб у блока - по логике геометрия у него та-же, только масштаб другой
Название: Re: Сравнение блоков
Отправлено: Вильдар от 11-11-2015, 17:02:41
Сравнивать нужно определения блоков.
Различия - изменение геометрии в блоке, добавление, удаление объектов и т.п.
Название: Re: Сравнение блоков
Отправлено: Александр Пекшев aka Modis от 11-11-2015, 17:27:56
Но, в этом случае большое значение имеет порядок объектов в блоке. При его изменении, но сохранении геометрии и расположения, вернет false
http://stackoverflow.com/questions/3669970/compare-two-listt-objects-for-equality-ignoring-order
Название: Re: Сравнение блоков
Отправлено: Вильдар от 11-11-2015, 18:58:02
Тогда только границ для сравнения недостаточно думаю. Нужно точнее определять объекты.
Может такой класс описания объекта добавлять в список и потом сравнивать:
Код - C# [Выбрать]
  1. class EntityInfo : IEquatable<EntityInfo>, IComparable<EntityInfo>
  2.    {
  3.       private Extents3d _extents;
  4.       private Guid _classId;
  5.       private Color _color;
  6.       private string _layer;
  7.       private string _linetype;
  8.       private LineWeight _lineweight;
  9.  
  10.       public EntityInfo(Entity ent)
  11.       {
  12.          _extents = ent.GeometricExtents;
  13.          _classId = ent.ClassID;
  14.          _color = ent.Color;
  15.          _layer = ent.Layer;
  16.          _linetype = ent.Linetype;
  17.          _lineweight = ent.LineWeight;        
  18.       }
  19.      
  20.  
  21.       public bool Equals(EntityInfo other)
  22.       {
  23.          if (Object.ReferenceEquals(other, null)) return false;
  24.          if (Object.ReferenceEquals(this, other)) return true;
  25.          return _extents.Equals(other._extents) &&
  26.             _classId.Equals(other._classId) &&
  27.             _color.Equals(other._color) &&
  28.             _layer.Equals(other._layer) &&
  29.             _linetype.Equals(other._linetype) &&
  30.             _lineweight.Equals(other._lineweight);
  31.       }
  32.  
  33.       public override int GetHashCode()
  34.       {
  35.          return _extents.GetHashCode() ^ _classId.GetHashCode() ^ _color?.GetHashCode() ?? 0 ^ _linetype?.GetHashCode() ?? 0 ^ _lineweight.GetHashCode();
  36.       }
  37.  
  38.       public int CompareTo(EntityInfo other)
  39.       {
  40.          int res = _extents.GetHashCode().CompareTo(other._extents.GetHashCode());
  41.          if (res == 0) res = _classId.CompareTo(other._classId);
  42.          if (res == 0) res = _color.CompareTo(other._color);
  43.          if (res == 0) res = _layer.CompareTo(other._layer);
  44.          if (res == 0) res = _linetype.CompareTo(other._linetype);
  45.          if (res == 0) res = _lineweight.CompareTo(other._lineweight);
  46.          return res;
  47.       }
  48.    }

Тестовый код:
Код - C# [Выбрать]
  1. // Тест сравнения блока в текущем чертеже и в файле
  2.       [CommandMethod("Test")]
  3.       public void Test()
  4.       {
  5.          Document doc = Application.DocumentManager.MdiActiveDocument;
  6.          Editor ed = doc.Editor;
  7.          Database db = doc.Database;
  8.  
  9.          bool result = false;        
  10.  
  11.          using (var t = db.TransactionManager.StartTransaction())
  12.          {            
  13.             // Тестовый блок в текущем чертеже
  14.             ObjectId idBtrTest = GetTestBtr(db);
  15.             List<EntityInfo> entsInfo = getEntInfoBtr(idBtrTest);
  16.  
  17.             // Тестовый блок во внешнем файле
  18.             using (var dbLib = new Database(false, true))
  19.             {
  20.                dbLib.ReadDwgFile(@"c:\test\Lib.dwg", FileShare.ReadWrite, false, "");
  21.                using (var tLib = dbLib.TransactionManager.StartTransaction())
  22.                {
  23.                   ObjectId idBtrTestLib = GetTestBtr(dbLib);
  24.                   List<EntityInfo> entsInfoLib = getEntInfoBtr(idBtrTestLib);
  25.                   // Сравнение блоков                    
  26.                   result = Enumerable.SequenceEqual(entsInfo.OrderBy(e => e), entsInfoLib.OrderBy(e => e)); //entsInfo.SequenceEqual(entsInfoLib);
  27.                   tLib.Commit();
  28.                }
  29.             }
  30.             t.Commit();
  31.          }
  32.          ed.WriteMessage("Результат сравнения блоков - {0}", result);
  33.       }
  34.  
  35.       private List<EntityInfo> getEntInfoBtr(ObjectId idBtrTest)
  36.       {
  37.          List<EntityInfo> entsInfo = new List<EntityInfo>();
  38.          var btr = idBtrTest.GetObject(OpenMode.ForRead) as BlockTableRecord;
  39.          foreach (ObjectId idEnt in btr)
  40.          {
  41.             var ent = idEnt.GetObject(OpenMode.ForRead) as Entity;
  42.             entsInfo.Add(new EntityInfo(ent));
  43.             if (ent is BlockReference)
  44.             {
  45.                entsInfo.AddRange(getEntInfoBtr(((BlockReference)ent).BlockTableRecord));
  46.             }
  47.          }
  48.          return entsInfo;
  49.       }
  50.  
  51.       private ObjectId GetTestBtr(Database db)
  52.       {
  53.          var bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
  54.          if (bt.Has("Test"))
  55.          {
  56.             return bt["Test"];
  57.          }
  58.          return ObjectId.Null;        
  59.       }
Название: Re: Сравнение блоков
Отправлено: Андрей Бушман от 11-11-2015, 19:46:55
пробовал сравнивать хеши определений блоков?
Название: Re: Сравнение блоков
Отправлено: Вильдар от 11-11-2015, 19:48:06
 ;D Нет  :-[
Сейчас попробую. спасибо.
Название: Re: Сравнение блоков
Отправлено: Андрей Бушман от 11-11-2015, 19:51:55
Я не утверждаю, что это сработает, но предположил как вариант. Если автодеск переопределила виртуальный метод Object.GetHashCode(), то имеет смысл попробовать, а если не переопределила, то скорее всего не поможет, т.к. у каждого объекта свои значения ObjectId и Handle, что наверняка повлияет на вычисление хеша в реализации, присутствующей по умолчанию.
Название: Re: Сравнение блоков
Отправлено: Вильдар от 11-11-2015, 19:55:21
Попробовал сравнить хеши - GetHashCode() у BlockTableRecord. Они разные для одинаковых блоков в разных файлах.
Просто, самый очевидный способ сразу не проверил. (
Название: Re: Сравнение блоков
Отправлено: Андрей Бушман от 11-11-2015, 20:00:47
http://adndevblog.typepad.com/autocad/2012/05/comparing-properties-of-two-entities.html
Название: Re: Сравнение блоков
Отправлено: Андрей Бушман от 11-11-2015, 20:19:34
Однако код по указанной мною ссылке я бы переписал... Метод, выполняющий сравнение, должен (имхо) принимать логический параметр, указывающий на то, как выполнять проверку свойств, содержащих в качестве значения ObjectId: нужно ли сравнивать только значения таких идентификаторов, или же необходимо получать объекты, на которые указывают идентификаторы и рекурсивно сравнивать их. При этом следует вести контроль того, какие идентификаторы уже проверялись, дабы избежать попадания в бесконечный цикл, т.к. объекты могут иметь взаимные ссылки др. на др.
Название: Re: Сравнение блоков
Отправлено: Вильдар от 11-11-2015, 20:27:05
Может сделаю сравнение производительности разных способов сравнения объектов.
Для своего случая можно не сравнивать все параметры объекта, а только нужных. Может это будет быстрее.
Спасибо.
Название: Re: Сравнение блоков
Отправлено: Александр Ривилис от 11-11-2015, 20:28:57
В этом коде как минимум придётся исключить проверки свойств ObjectId. И вообще подозреваю, что с таким кодом проверка будет длится вечность... Не говоря уже о том сколько исключений будет при вызове свойств...
Кстати, этот вопрос (сравнение чертежей) мы кажется уже когда-то обсуждали.
Название: Re: Сравнение блоков
Отправлено: Андрей Бушман от 11-11-2015, 20:30:31
В этом коде как минимум придётся исключить проверки свойств ObjectId.
я выше писал насчёт ObjectId.
Название: Re: Сравнение блоков
Отправлено: Андрей Бушман от 11-11-2015, 20:34:54
Не говоря уже о том сколько исключений будет при вызове свойств...
Сколько? :)
Название: Re: Сравнение блоков
Отправлено: Александр Ривилис от 11-11-2015, 20:50:00
Сколько? :)
Будет зависеть от того какие примитивы в блоке. Помнишь такое исключение, как NotApplicable?
Или самое любимое: NotImplementedYet  ? ;)

Название: Re: Сравнение блоков
Отправлено: Дима_ от 11-11-2015, 20:50:12
На двг.ру была тема на несколько страниц - ИХМО - в общем случае не реально, не так давно тут разбиралась тема по сравнению ОДНОГО тела в заранее определенной форме - цилиндр - и то были разночтения. Потом допусков которые надо проверить - тьма - даже одна линия может быть направленна в разные стороны. Вероятностный метод, достаточный для конкретных условий, вполне возможен - вот с их формализации и надо начинать.
Название: Re: Сравнение блоков
Отправлено: Александр Ривилис от 11-11-2015, 20:52:28
Если нужно сравнивать внешний вид блоков, то нужно сравнивать внешний вид, а не содержимое. И тогда подход может быть такой: печатаем блоки в растр и сравниваем растры. Кстати, эта тема может помочь: http://adn-cis.org/forum/index.php?topic=2497
Название: Re: Сравнение блоков
Отправлено: Александр Ривилис от 11-11-2015, 21:16:54
Еще один вариант, который я не проверял: Autodesk.AutoCAD.Internal.DrawingCompare
Но в нём сравниваются файлы, так что блоки придётся выпихивать во внешние файлы и их сравнивать. Так как класс и методы Internal, то описание что они делают вряд ли где-то найдёшь.
Название: Re: Сравнение блоков
Отправлено: Александр Пекшев aka Modis от 11-11-2015, 22:20:15
ИМХО:
Я думаю, что универсального метода сравнения двух блоков в двух разных файлах просто не существует! Например будут у меня в блоке две цветные линии - поменяю я им порядок прорисовки и что: геометрия одинаковая, но метод проверки через растры вернет false
Или например - два совершенно одинаковых блока, но я возьму и поменяю одному из них имя: уверен, что Autodesk.AutoCAD.Internal.DrawingCompare вернет false
Ну и так далее - можно вариантов придумать массу
Если автор задается таким вопросом, то скорее всего об этих блоках ему известна какая-либо информация. Возможно его же приложение создает эти блоки. Тогда ему остается вариант, который он и придумал изначально. Просто нужно будет наполнить его максимальным количеством свойств (опять таки - нужных автору) для проверки
Название: Re: Сравнение блоков
Отправлено: Владимир Шу от 12-11-2015, 07:45:22
Как можно сравнить два определения блока? Одинаковая ли у них геометрия.
Мне нужно сравнивать блоки в текущем чертеже и во внешнем файле.
И тогда подход может быть такой: печатаем блоки в растр и сравниваем растры
Добавлю, в  чб формате и с обнуленными атрибутами. ИМХО, самый верный способ.
ЗЫ.
Сравнивается геометрия, так что слой, цвет, и пр. свойство не имеют значения.