Как правильно скопировать объекты на лист без видового экрана?

Автор Тема: Как правильно скопировать объекты на лист без видового экрана?  (Прочитано 4583 раз)

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

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн geosysАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 3
Доброго времени суток!
Есть задача скопировать выделенный набор объектов на лист без видового экрана, при этом вместить их в рамку на листе (соответственно изменяя масштаб самих объектов относительно размера их границ и листа). Копирую, соответственно по оригинальным координатам всё отлетает в другую сторону от листа. Собственно вопрос, как скопировать все эти объекты в лист и, не нарушая их целостности и структуры, уменьшить/увеличить их так, чтобы они вмещались в его границы?
Код - C# [Выбрать]
  1.  [CommandMethod("test")]
  2.  public static void copytolist()
  3.  {
  4.         Document doc = Application.DocumentManager.MdiActiveDocument;
  5.         Editor ed = doc.Editor;
  6.         Database db = doc.Database;
  7.         //start transaction
  8.         Transaction tr = doc.TransactionManager.StartTransaction();
  9.         using (tr)
  10.         {
  11.         //get the crossing windows coordinates:
  12.         PromptPointOptions ppo = new PromptPointOptions("\nFirst corner: ");
  13.         PromptPointResult ppr = ed.GetPoint(ppo);
  14.         if (ppr.Status != PromptStatus.OK) return;
  15.         PromptCornerOptions pco = new PromptCornerOptions("\nOther corner: ", ppr.Value);
  16.         PromptPointResult pcr = ed.GetCorner(pco);
  17.         if (pcr.Status != PromptStatus.OK) return;
  18.         Point3d p1 = ppr.Value;
  19.         Point3d p2 = pcr.Value;
  20.         if (p1.X == p2.X || p1.Y == p2.Y)
  21.             {
  22.                 ed.WriteMessage("\nInvalid coordinate specification");
  23.                 return;
  24.             }
  25.  
  26.         //crossing windows from coordinates:
  27.         PromptSelectionResult res = ed.SelectCrossingWindow(p1, p2);
  28.         if (res.Status != PromptStatus.OK)
  29.             return;
  30.         SelectionSet sset = res.Value;
  31.         if (sset.Count == 0)
  32.              return;
  33.  
  34.                 // take all the entities from the cross selection:
  35.                 ObjectIdCollection ids = new ObjectIdCollection();
  36.                 foreach (SelectedObject obj in sset)
  37.                     ids.Add(obj.ObjectId);
  38.  
  39.                 //gettin and settin desired layout and clone objects
  40.                 LayoutManager acLayoutMgr = LayoutManager.Current;
  41.                 DBDictionary layoutDic = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForWrite, false) as DBDictionary;
  42.                 foreach (DBDictionaryEntry entry in layoutDic)
  43.                 {
  44.                         ObjectId layoutId = entry.Value;
  45.                         Layout layout = tr.GetObject(layoutId, OpenMode.ForWrite) as Layout;
  46.  
  47.                         if (layout.LayoutName == "test")
  48.                         {
  49.                                 acLayoutMgr.CurrentLayout=layout.LayoutName;
  50.                                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(layout.BlockTableRecordId, OpenMode.ForWrite);
  51.                                 IdMapping acIdMap = new IdMapping();
  52.                                 //verify if there are entities to be copied:
  53.                 if (ids.Count > 0)
  54.                         {
  55.                             ed.WriteMessage("\nYES, there are entities to be copied: {0} entities!", ids.Count);
  56.                             db.DeepCloneObjects(ids, btr.ObjectId, acIdMap, false);
  57.                         }
  58.                 else
  59.                         ed.WriteMessage("\nNO, there are no entities to be copied!");
  60.                                 //loop through the newly created (clone) objects
  61.                                 foreach (ObjectId clone in ids)
  62.                         {
  63.                                 IdPair pair1 = acIdMap[clone];
  64.                                 Entity ent = tr.GetObject(pair1.Value, OpenMode.ForWrite) as Entity;
  65.                                                 //жду предложений
  66.                                                 ////////////////////////////////////////////////////////////////
  67.                         }
  68.                         }
  69.                 }
  70.                 tr.Commit();
  71.         }
  72.  }
Мои размышления:
У типа Entity нет свойства "Position" или "Location" а в наборе объектов у меня будут полилинии, мтекст, круги и штриховки. У меня были мысли сделать это так: создать блок из этих объектов и используя Position поместить в центр листа, расчитав его к примеру, используя layout.PlotWindowAreа (что будет не совсем корректно), потом разбить блок обратно, но в таком случае непонятно, как быть с изменением масштаба объектов и вписанием их в рамку, да и очень много лишних действий. Может кто-то знает более элегантный путь решений данной задачи?

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
расчитав его к примеру, используя layout.PlotWindowAreа (что будет не совсем корректно)
У тебя есть рамка (полилиния) на листе. Её можно использовать в качестве области, в которую нужно вписать скопированные из модели примитивы?
« Последнее редактирование: 23-05-2018, 17:31:34 от Александр Ривилис »
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
У типа Entity нет свойства "Position" или "Location" а в наборе объектов у меня будут полилинии, мтекст, круги и штриховки.
Зато есть габаритный контейнер (Entity.GeometricExtents), который ты и можешь использовать.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн geosysАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 3
расчитав его к примеру, используя layout.PlotWindowAreа (что будет не совсем корректно)
У тебя есть рамка (полилиния) на листе. Её можно использовать в качестве области, в которую нужно вписать скопированный из модели примитивы?
Да конечно, можно даже её сделать блоком, чтобы всегда можно было легко искать его, скажем, по имени.

У типа Entity нет свойства "Position" или "Location" а в наборе объектов у меня будут полилинии, мтекст, круги и штриховки.
Зато есть габаритный контейнер (Entity.GeometricExtents), который ты и можешь использовать.
Хм... и, агрегируя границы всех объектов переместить их, используя Entity.GeometricExtents.Set? Хотя для группы объектов так не получится если я не ошибаюсь. Видно придется перемещать по одному в цикле... Сейчас попробую покумекать, о затыках напишу. Спасибо.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
используя Entity.GeometricExtents.Set?
Нет конечно.
1. Нужно, как и в задаче с видовым экраном, найти Extents3d, в который помещаются все выбранные примитивы:
Код - C# [Выбрать]
  1. Extents3d ext = new Extents3d();
  2. foreach (ObjectId id in ids)
  3. {
  4.   Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
  5.   if (ent != null)
  6.   {
  7.     ext.AddExtents(ent.GeometricExtents);
  8.   }
  9. }
2. Найти Extents3d extLayout для рамки на листе.
3. Скопировать примитивы с модели в лист (у тебя уже это есть в коде) и сохранить ObjectId для этих клонов.
4. Найти вектор переноса ext.MinPoint в extLayout.MinPoint
5. Создать матрицу преобразования на основе вектора переноса из пункта 4.
6. Преобразовать все клоны по этой матрице используя метод Entity.TransformBy
7. Вычислить масштаб преобразования (используя ext и extLayout и оценку высоты и ширины для них).
8. Создать матрицу преобразования на основе масштабирования из пункта 7. В качестве базовой точки использовать extLayout.MinPoint
9. Преобразовать все клоны по этой матрице используя метод Entity.TransformBy
Собственно говоря и весь алгоритм. В принципе преобразование можно сделать однократное если получить сразу матрицу переобразования с переносом и масштабированием.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение geosys 24-05-2018, 13:39:23

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Алгоритм немного изменил и уточнил:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.EditorInput;
  6.  
  7. // This line is not mandatory, but improves loading performances
  8. [assembly: CommandClass(typeof(CloneToLayout.Utils))]
  9.  
  10. namespace CloneToLayout
  11. {
  12.  
  13.   public class Utils
  14.   {
  15.     [CommandMethod("CloneToLayout")]
  16.     public void CloneToLayout()
  17.     {
  18.       Document doc = Application.DocumentManager.MdiActiveDocument;
  19.       if (doc == null) return;
  20.       Editor ed = doc.Editor;
  21.       Database db = doc.Database;
  22.       PromptSelectionResult rs = ed.GetSelection();
  23.       if (rs.Status != PromptStatus.OK) return;
  24.       ObjectIdCollection ids = new ObjectIdCollection(rs.Value.GetObjectIds());
  25.       testClone(ref ids);
  26.     }
  27.  
  28.     public static void testClone(ref ObjectIdCollection ids)
  29.     {
  30.       Document doc = Application.DocumentManager.MdiActiveDocument;
  31.       Editor ed = doc.Editor;
  32.       Database db = doc.Database;
  33.       Extents3d ext = new Extents3d();
  34.       using (Transaction tr = doc.TransactionManager.StartTransaction())
  35.       {
  36.         foreach (ObjectId id in ids)
  37.         {
  38.           Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
  39.           if (ent != null) {
  40.             ext.AddExtents(ent.GeometricExtents);
  41.           }
  42.         }
  43.  
  44.         // Центральная точка габаритов выбранных примитивов.
  45.         Point3d modelCenter = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5;
  46.         double modelHeight = ext.MaxPoint.Y - ext.MinPoint.Y;
  47.         double modelWidth  = ext.MaxPoint.X - ext.MinPoint.X;
  48.  
  49.         LayoutManager layman = LayoutManager.Current;
  50.  
  51.         DBDictionary layoutDic = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead) as DBDictionary;
  52.  
  53.         double mScale = 1;
  54.  
  55.         // Проходимся по всем листам (модель пропускаем)
  56.         foreach (DBDictionaryEntry entry in layoutDic)
  57.         {
  58.           Layout layout = tr.GetObject(entry.Value, OpenMode.ForRead) as Layout;
  59.  
  60.           if (!layout.ModelType) // Это не модель
  61.           {
  62.             Extents3d extLayout = new Extents3d();
  63.             BlockTableRecord btrLayout = tr.GetObject(layout.BlockTableRecordId, OpenMode.ForRead) as BlockTableRecord;
  64.             // Находим габариты примитивов в листе - будем считать их рамкой листа.
  65.             foreach (ObjectId id in btrLayout)
  66.             {
  67.               Entity ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
  68.               if (ent != null && !(ent is Viewport))
  69.               {
  70.                 extLayout.AddExtents(ent.GeometricExtents);
  71.               }
  72.             }
  73.             // Центральная точка габаритов выбранных примитивов в листе
  74.             Point3d layoutCenter = extLayout.MinPoint + (extLayout.MaxPoint - extLayout.MinPoint) * 0.5;
  75.             double layoutHeight = extLayout.MaxPoint.Y - extLayout.MinPoint.Y;
  76.             double layoutWidth = extLayout.MaxPoint.X - extLayout.MinPoint.X;
  77.  
  78.             // Находим масштабный коэффициент.
  79.             mScale = layoutWidth / modelWidth;
  80.             // Если масштабированная высота модели превышает высоту листа,
  81.             // то в качестве масштаба используем коэффициент на основе высоты
  82.             if (mScale * modelHeight > layoutHeight)
  83.               mScale = layoutHeight / modelHeight;
  84.  
  85.             // Матрица масштабирования относительно центра в модели
  86.             Matrix3d matScale = Matrix3d.Scaling(mScale, modelCenter);
  87.             // Масштабируем старый центр в модели по этой матрице
  88.             Point3d  modelCenterScaling = modelCenter.TransformBy(matScale);
  89.             // Находим матрицу переноса точки масштабированного центра в модели в точку
  90.             // центра листа
  91.             Matrix3d matDisp = Matrix3d.Displacement(layoutCenter - modelCenterScaling);
  92.             // Комбинированная матрица
  93.             Matrix3d matCompose = matScale.PreMultiplyBy(matDisp);
  94.  
  95.             IdMapping idmap = new IdMapping();
  96.  
  97.             btrLayout.UpgradeOpen();
  98.             db.DeepCloneObjects(ids, layout.BlockTableRecordId, idmap, false);
  99.             btrLayout.DowngradeOpen();
  100.             foreach (IdPair idpair in idmap)
  101.             {
  102.               if (idpair.IsPrimary)
  103.               {
  104.                 Entity ent = tr.GetObject(idpair.Value, OpenMode.ForWrite) as Entity;
  105.                 if (ent != null)
  106.                 {
  107.                   // Трансформируем по комбинированной матрице
  108.                   ent.TransformBy(matCompose);
  109.                 }
  110.               }
  111.             }
  112.           }
  113.  
  114.         }
  115.         tr.Commit();
  116.       }
  117.     }
  118.   }
  119. }



P.S.: Напоминаю замечание Дмитрия Загорулькина о том, как правильно (более точно) находить габариты примитивов вместо свойства Entity.GeometricExtents: http://adn-cis.org/forum/index.php?topic=2933.msg12071#msg12071
« Последнее редактирование: 25-05-2018, 00:02:03 от Александр Ривилис »
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн geosysАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 3
Александр Ривилис, супер! Спасибо огромное! Неожидал такого быстрого, профессионального и грамотного решения!
Про финт с матрицами никогда бы сам недогадался :)

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Неожидал такого быстрого, профессионального и грамотного решения!
Ну так не забывай на каком форуме задаёшь вопросы... :)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн geosysАвтор темы

  • ADN OPEN
  • Сообщений: 25
  • Карма: 3
Неожидал такого быстрого, профессионального и грамотного решения!
Ну так не забывай на каком форуме задаёшь вопросы... :)
Так поэтому я не ожидал :) Обычно на русскоязычных форумах помощи не дождешься (я даже сейчас не про Автокад, а вообще в целом, в области программирования, например), приходилось писать забугор, но Ваш ресурс это исключение :)