Audit выявляет ошибки после использования HandOverTo

Автор Тема: Audit выявляет ошибки после использования HandOverTo  (Прочитано 7597 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
AutoCAD 2009 SP3 Enu x86\x64

Имеется простой код, заменяющий объекты Line на объекты Polyline3d, поделённые на указанное число равных частей (посредством дополнительных вершин Point3d).

Первая версия кода, в которой результирующие объекты получают новые значения идентификаторов (ObjectId):

Код - C# [Выбрать]
  1. // divide.cs
  2. // © Andrey Bushman, 2014
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.IO;
  8.  
  9. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  10. using App = Autodesk.AutoCAD.ApplicationServices;
  11. using Ed = Autodesk.AutoCAD.EditorInput;
  12. using Db = Autodesk.AutoCAD.DatabaseServices;
  13. using Gem = Autodesk.AutoCAD.Geometry;
  14. using Rtm = Autodesk.AutoCAD.Runtime;
  15.  
  16. [assembly: Rtm.CommandClass(typeof(Bushman.CAD.Commands.Divide))]
  17.  
  18. namespace Bushman.CAD.Commands {
  19.  
  20.         public class Divide : Rtm.IExtensionApplication {
  21.  
  22.                 const String ns = "Bushman";
  23.                 static Int32 div_count = 2; // по умолчанию предлагаю делить линии на две части.
  24.                 const String msg0 = "Указанный набор не содержит объектов Line.\n";
  25.                 const String msg = "Выполнение команды прервано.\n";
  26.  
  27.                 /// <summary>
  28.                 /// заменяет указанные пользователем линии 3D-полилиниями, поделив
  29.                 /// каждую из этих полилиний на заданное количество равных сегментов
  30.                 /// посредством дополнительных вершин.
  31.                 /// </summary>
  32.                 [Rtm.CommandMethod(ns, "divide2", Rtm.CommandFlags.UsePickSet | Rtm.CommandFlags.Redraw
  33.                         | Rtm.CommandFlags.Modal | Rtm.CommandFlags.NoPaperSpace)]
  34.                 public void Divide2() {
  35.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  36.                         Ed.Editor ed = doc.Editor;
  37.                         Db.Database db = doc.Database;
  38.                         Ed.PromptSelectionResult psr;
  39.                         psr = ed.SelectImplied();
  40.                         Db.ObjectId[] ids = null;
  41.  
  42.                         if (psr.Status == Ed.PromptStatus.OK) {
  43.                                 ids = psr.Value.GetObjectIds().Where(id => id.ObjectClass.Name == "AcDbLine")
  44.                                         .ToArray();
  45.                                 if (ids != null && ids.Length > 0) ed.SetImpliedSelection(ids);
  46.                                 else {
  47.                                         ed.WriteMessage(msg0);
  48.                                         ed.WriteMessage(msg);
  49.                                         return;
  50.                                 }
  51.                         }
  52.                         else {
  53.                                 Db.TypedValue[] typeValues = new Db.TypedValue[1];
  54.                                 typeValues.SetValue(new Db.TypedValue((Int32)Db.DxfCode.Start, "LINE"), 0);
  55.                                 Ed.SelectionFilter filter = new Ed.SelectionFilter(typeValues);
  56.                                 psr = ed.GetSelection(filter);
  57.                         }
  58.  
  59.                         if (psr.Status != Ed.PromptStatus.OK) {
  60.                                 ed.WriteMessage(msg);
  61.                                 return;
  62.                         }
  63.  
  64.                         if (ids == null) ids = psr.Value.GetObjectIds();
  65.  
  66.  
  67.                         Ed.PromptIntegerOptions pio = new Ed.PromptIntegerOptions("Количество сегментов");
  68.                         pio.AllowNegative = false;
  69.                         pio.AllowNone = false;
  70.                         pio.AllowZero = false;
  71.                         pio.DefaultValue = div_count;
  72.  
  73.                         Ed.PromptIntegerResult result = ed.GetInteger(pio);
  74.                         if (result.Status != Ed.PromptStatus.OK) {
  75.                                 ed.WriteMessage(msg);
  76.                                 return;
  77.                         }
  78.                         else {
  79.                                 div_count = result.Value;
  80.                         }
  81.  
  82.                         List<Db.ObjectId> resultIds = new List<Db.ObjectId>();
  83.  
  84.                         using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  85.                                 foreach (Db.ObjectId id in ids) {
  86.                                         Db.Line line = (Db.Line)tr.GetObject(id, Db.OpenMode.ForWrite);
  87.                                         Gem.Point3dCollection points = new Gem.Point3dCollection();
  88.                                         Gem.Point3d point = line.StartPoint;
  89.                                         points.Add(line.StartPoint);
  90.                                         Double dx = line.Delta.X / div_count;
  91.                                         Double dy = line.Delta.Y / div_count;
  92.                                         Double dz = line.Delta.Z / div_count;
  93.                                         for (int i = 1; i < div_count; i++) {
  94.                                                 point = new Gem.Point3d(point.X + dx, point.Y + dy, point.Z + dz);
  95.                                                 points.Add(point);
  96.                                         }
  97.                                         points.Add(line.EndPoint);
  98.  
  99.                                         Db.Polyline3d pline = new Db.Polyline3d(Db.Poly3dType.SimplePoly, points,
  100.                                                 false);
  101.                                         pline.SetDatabaseDefaults();
  102.                                         pline.LayerId = line.LayerId;
  103.                                         pline.Color = line.Color;
  104.                                         pline.ColorIndex = line.ColorIndex;
  105.                                         pline.LinetypeId = line.LinetypeId;
  106.                                         pline.LinetypeScale = line.LinetypeScale;
  107.                                         pline.LineWeight = line.LineWeight;
  108.  
  109.                                         line.Erase(true);
  110.  
  111.                                         Db.BlockTable bt = (Db.BlockTable)tr.GetObject(db.BlockTableId,
  112.                                                 Db.OpenMode.ForRead);
  113.                                         Db.BlockTableRecord btr = (Db.BlockTableRecord)tr.GetObject(
  114.                                                 bt[Db.BlockTableRecord.ModelSpace], Db.OpenMode.ForWrite);
  115.                                         btr.AppendEntity(pline);
  116.                                         tr.AddNewlyCreatedDBObject(pline, true);
  117.                                         resultIds.Add(pline.ObjectId);
  118.                                 }
  119.                                 tr.Commit();
  120.                         }
  121.                         // Результат преобразования выделяю "ручками"
  122.                         ed.SetImpliedSelection(resultIds.ToArray());
  123.                 }
  124.  
  125.                 #region IExtensionApplication Members
  126.  
  127.                 public void Initialize() {
  128.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  129.                         Ed.Editor ed = doc.Editor;
  130.                         ed.WriteMessage("\n{0}. © Andrey Bushman, 2014\n\n",
  131.                                 Path.GetFileName(this.GetType().Assembly.Location));
  132.                 }
  133.  
  134.                 public void Terminate() {
  135.                         // throw new NotImplementedException();
  136.                 }
  137.                 #endregion
  138.         }
  139. }

Обозначенный выше код тестирую на файле div2.dwg (см. вложение). Получаю ожидаемый результат, после чего проверяю файл командой _AUDIT:
Цитата: AutoCAD Console
Command: netload
div2.dll. © Andrey Bushman, 2014


Command: divide2

Select objects: Specify opposite corner: 9 found

Select objects:  Количество сегментов <2>: 4

Command: audit

Fix any errors detected? [Yes/No] <N>:



Auditing Header


Auditing Tables


Auditing Entities Pass 1

Pass 1 1900    objects audited
Auditing Entities Pass 2

Pass 2 1900    objects audited
Auditing Blocks

 42      Blocks audited

Total errors found 0 fixed 0

Erased 0 objects

Как видим, проблем нет.

Но... Дело в том, что исходные линии могут иметь некоторые расширенные данные (XRecord и ExtensionDictionary). Было бы правильным передать эти данные новому примитиву, создаваемому взамен исходного. Кроме того, на свойства исходного примитива в чертеже могли быть ссылки из полей (Field)... Соответственно, объект Polyline3d, создаваемый взамен Line должен получать то же самое значение ObjectId, чтобы по возможности исключить появление #### в полях. В свете оного немного подправляю свой исходный код и получаю следующий вариант:

Код - C# [Выбрать]
  1. // divide.cs
  2. // © Andrey Bushman, 2014
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.IO;
  8.  
  9. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  10. using App = Autodesk.AutoCAD.ApplicationServices;
  11. using Ed = Autodesk.AutoCAD.EditorInput;
  12. using Db = Autodesk.AutoCAD.DatabaseServices;
  13. using Gem = Autodesk.AutoCAD.Geometry;
  14. using Rtm = Autodesk.AutoCAD.Runtime;
  15.  
  16. [assembly: Rtm.CommandClass(typeof(Bushman.CAD.Commands.Divide))]
  17.  
  18. namespace Bushman.CAD.Commands {
  19.  
  20.         public class Divide : Rtm.IExtensionApplication {
  21.  
  22.                 const String ns = "Bushman";
  23.                 static Int32 div_count = 2; // по умолчанию предлагаю делить линии на две части.
  24.                 const String msg0 = "Указанный набор не содержит объектов Line.\n";
  25.                 const String msg = "Выполнение команды прервано.\n";
  26.  
  27.                 /// <summary>
  28.                 /// заменяет указанные пользователем линии 3D-полилиниями, поделив
  29.                 /// каждую из этих полилиний на заданное количество равных сегментов
  30.                 /// посредством дополнительных вершин.
  31.                 /// </summary>
  32.                 [Rtm.CommandMethod(ns, "divide2", Rtm.CommandFlags.UsePickSet | Rtm.CommandFlags.Redraw
  33.                         | Rtm.CommandFlags.Modal | Rtm.CommandFlags.NoPaperSpace)]
  34.                 public void Divide2() {
  35.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  36.                         Ed.Editor ed = doc.Editor;
  37.                         Db.Database db = doc.Database;
  38.                         Ed.PromptSelectionResult psr;
  39.                         psr = ed.SelectImplied();
  40.                         Db.ObjectId[] ids = null;
  41.  
  42.                         if (psr.Status == Ed.PromptStatus.OK) {
  43.                                 ids = psr.Value.GetObjectIds().Where(id => id.ObjectClass.Name == "AcDbLine")
  44.                                         .ToArray();
  45.                                 if (ids != null && ids.Length > 0) ed.SetImpliedSelection(ids);
  46.                                 else {
  47.                                         ed.WriteMessage(msg0);
  48.                                         ed.WriteMessage(msg);
  49.                                         return;
  50.                                 }
  51.                         }
  52.                         else {
  53.                                 Db.TypedValue[] typeValues = new Db.TypedValue[1];
  54.                                 typeValues.SetValue(new Db.TypedValue((Int32)Db.DxfCode.Start, "LINE"), 0);
  55.                                 Ed.SelectionFilter filter = new Ed.SelectionFilter(typeValues);
  56.                                 psr = ed.GetSelection(filter);
  57.                         }
  58.  
  59.                         if (psr.Status != Ed.PromptStatus.OK) {
  60.                                 ed.WriteMessage(msg);
  61.                                 return;
  62.                         }
  63.  
  64.                         if (ids == null) ids = psr.Value.GetObjectIds();
  65.  
  66.  
  67.                         Ed.PromptIntegerOptions pio = new Ed.PromptIntegerOptions("Количество сегментов");
  68.                         pio.AllowNegative = false;
  69.                         pio.AllowNone = false;
  70.                         pio.AllowZero = false;
  71.                         pio.DefaultValue = div_count;
  72.  
  73.                         Ed.PromptIntegerResult result = ed.GetInteger(pio);
  74.                         if (result.Status != Ed.PromptStatus.OK) {
  75.                                 ed.WriteMessage(msg);
  76.                                 return;
  77.                         }
  78.                         else {
  79.                                 div_count = result.Value;
  80.                         }
  81.  
  82.                         // Имитирую транзакцию, поскольку HandOverTo можно использовать только для объектов,
  83.                         // полученных вне транзакции
  84.                         using (Db.OpenCloseTransaction tr = db.TransactionManager.StartOpenCloseTransaction()) {
  85.                                 foreach (Db.ObjectId id in ids) {
  86.                                         Db.Line line = (Db.Line)tr.GetObject(id, Db.OpenMode.ForWrite);
  87.                                         Gem.Point3dCollection points = new Gem.Point3dCollection();
  88.                                         Gem.Point3d point = line.StartPoint;
  89.                                         points.Add(line.StartPoint);
  90.                                         Double dx = line.Delta.X / div_count;
  91.                                         Double dy = line.Delta.Y / div_count;
  92.                                         Double dz = line.Delta.Z / div_count;
  93.                                         for (int i = 1; i < div_count; i++) {
  94.                                                 point = new Gem.Point3d(point.X + dx, point.Y + dy, point.Z + dz);
  95.                                                 points.Add(point);
  96.                                         }
  97.                                         points.Add(line.EndPoint);
  98.  
  99.                                         Db.Polyline3d pline = new Db.Polyline3d(Db.Poly3dType.SimplePoly, points,
  100.                                                 false);
  101.                                         pline.SetDatabaseDefaults();
  102.                                         pline.LayerId = line.LayerId;
  103.                                         pline.Color = line.Color;
  104.                                         pline.ColorIndex = line.ColorIndex;
  105.                                         pline.LinetypeId = line.LinetypeId;
  106.                                         pline.LinetypeScale = line.LinetypeScale;
  107.                                         pline.LineWeight = line.LineWeight;
  108.  
  109.                                         // Подменяю идентификатор и назначаю расширенные данные исходного объекта
  110.                                         line.HandOverTo(pline, true, true);
  111.                                 }
  112.                                 tr.Commit();
  113.                         }
  114.                         // Результат преобразования выделяю "ручками"
  115.                         ed.SetImpliedSelection(ids);
  116.                 }
  117.  
  118.                 #region IExtensionApplication Members
  119.  
  120.                 public void Initialize() {
  121.                         App.Document doc = cad.DocumentManager.MdiActiveDocument;
  122.                         Ed.Editor ed = doc.Editor;
  123.                         ed.WriteMessage("\n{0}. © Andrey Bushman, 2014\n\n",
  124.                                 Path.GetFileName(this.GetType().Assembly.Location));
  125.                 }
  126.  
  127.                 public void Terminate() {
  128.                         // throw new NotImplementedException();
  129.                 }
  130.                 #endregion
  131.         }
  132. }

Однако результат получаю не совсем тот, который ожидается: в результате полученные примитивы выделяются пунктиром, но ручки не появляются. Для того, чтобы ручки появились, требуется либо выполнить операцию сохранения чертежа, либо переключиться на др вкладку Layout\Model и обратно. После этого выделение созданных мною программно 3d-полилиний работает как обычно: конечные объекты выделяются пунктиром и отображаются ручки.

Если после выполнения второго варианта кода запустить команду _AUDIT, то будет найдено 9 ошибок (по количеству созданных примитивов). Но после первого же сохранения чертежа эти ошибки исчезают, а так же исчезает и обозначенная мною выше проблема с ручками:

Цитата: AutoCAD Console
Command: netload
div2.dll. © Andrey Bushman, 2014


Command: divide2

Select objects: Specify opposite corner: 9 found

Select objects:
Количество сегментов <2>: 4

Command: audit

Fix any errors detected? [Yes/No] <N>:



Auditing Header


Auditing Tables


Auditing Entities Pass 1

Pass 1 1900    objects audited
Auditing Entities Pass 2

Pass 2 1800    objects auditedAcDb3dPolyline(1FBF5)             Sequence End
Null or invalid; Not fixed
AcDb3dPolyline(1FBF6)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FBF7)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FBF8)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FC73)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FC74)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FC75)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FC76)             Sequence End Null or invalid; Not fixed
AcDb3dPolyline(1FC77)             Sequence End Null or invalid; Not fixed

Pass 2 1900    objects audited
Auditing Blocks


 42      Blocks audited

Total errors found 9 fixed 0

Erased 0 objects


Command:
Command:
Command: _qsave
Command:
Command:
Command: *Cancel*

Command: audit

Fix any errors detected? [Yes/No] <N>:



Auditing Header


Auditing Tables


Auditing Entities Pass 1

Pass 1 1900    objects audited
Auditing Entities Pass 2

Pass 2 1900    objects audited
Auditing Blocks

 42      Blocks audited

Total errors found 0 fixed 0

Erased 0 objects

Вопрос 1: Что является причиной ошибок во втором варианте кода?

P.S. В файле div2.dwg  присутствует (для примера) поле. Так вот после превращения линии в 3d-полилинию (посредством второго варианта кода) и изменении длины примитива вручную (нужный примитив выделен красным цветом), обновление поля не происходит (аудит и регенерация не помогают). Но если документ сохранить, закрыть и снова открыть, то теперь обновление поля будет происходить успешно. 

Вопрос 2: В чём причина такого поведения поля?

Спасибо.
« Последнее редактирование: 29-01-2014, 14:37:36 от Андрей Бушман »

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Разобрался. Я думал, что если объекты редактируются вне транзакции, то нет необходимости вызывать метод
Код - C# [Выбрать]
  1. tr.AddNewlyCreatedDBObject(pline,true);
Оказывается нужно... Если данную строку кода записать после вызова HandOverTo, то всё начинает работать как нужно (в т.ч. и поля) и ошибок audit не находит:
Код - C# [Выбрать]
  1. line.HandOverTo(pline, true, true);
  2. tr.AddNewlyCreatedDBObject(pline,true);

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Оказывается нужно... Если данную строку кода записать после вызова HandOverTo, то всё начинает работать как нужно (в т.ч. и поля) и ошибок audit не находит:
Отличная находка! Необходимость вызова tr.AddNewlyCreatedDBObject(pline,true); совсем неочевидна.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
В виду обнаруженной особенности, я подправил эту заметку, а так же внёс небольшие коррективы в эту. В частности, в заметке, указанной в первой ссылке, я показал альтернативную реализацию метода CircleToArc: с использованием эмуляции транзакции и без этой эмуляции.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Андрей. В последнем твоем коде не хватает circle.Dispose() для предотвращения утечки памяти.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Андрей. В последнем твоем коде не хватает circle.Dispose() для предотвращения утечки памяти.
Спасибо. Да, действительно память не освобождена... Нужно было самому проверить :) Подумал, что раз  HandOverTo удаляет объект, то и память тоже освобождает. Подправил код и в тексте добавил информацию о том, что Dispose следует выполнять вручную, т.к.  HandOverTo этого не делает.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Андрей. В последнем твоем коде не хватает circle.Dispose() для предотвращения утечки памяти.
Кстати, не только в последнем, но и в первом варианте реализации метода CircleToArc :) Исправил в обоих.