Разбить чертеж по листам и обрезать модель .net

Автор Тема: Разбить чертеж по листам и обрезать модель .net  (Прочитано 9101 раз)

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

Оффлайн Andrey-AdamenkoАвтор темы

  • ADN OPEN
  • Сообщений: 13
  • Карма: 0
  • CAD Manager
Здравствуйте!

Я пытаюсь сделать приложение для разбивки чертежа по листам и сейчас мне необходимо скопировать чертеж по количеству листов и удалить из каждого получившегося "лишние" листы.

Сделать это желательно в фоновом режиме, то есть не загружая его в DocumentManager.

Вот код:
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Clear drawing from unnecessary sheets
  3. /// </summary>
  4. /// <param name="dwgName">Full drawind name</param>
  5. /// <param name="target">Target layout name</param>
  6. private static void CleanLayouts(string dwgName, string target)
  7. {
  8.         Database curDwg = new Database();
  9.        
  10.         curDwg.ReadDwgFile(dwgName, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
  11.        
  12.         using (Transaction operateDwgTrans = curDwg.TransactionManager.StartTransaction())
  13.         {
  14.                 DBDictionary spaces = operateDwgTrans.GetObject(curDwg.LayoutDictionaryId, OpenMode.ForWrite) as DBDictionary;
  15.                
  16.                 ObjectIdCollection idsToRemove = new ObjectIdCollection();
  17.                
  18.                 foreach (DBDictionaryEntry curSpace in spaces)
  19.                 {
  20.                         ObjectId curSpaceId = curSpace.Value;
  21.                        
  22.                         Layout acadLayout = operateDwgTrans.GetObject(curSpaceId, OpenMode.ForWrite) as Layout;
  23.                        
  24.                         if (acadLayout.LayoutName == "Model") continue;
  25.                        
  26.                         if (acadLayout.LayoutName != target)
  27.                         {
  28.                                 // Удаляю Layout
  29.                                 idsToRemove.Add(curSpaceId);
  30.                                
  31.                                 // Удаляю BlockTableRecord. Это я уже от безвыходности добавил
  32.                                 idsToRemove.Add(acadLayout.BlockTableRecordId);
  33.                         }
  34.                 }
  35.                                
  36.                 foreach (ObjectId id in idsToRemove)
  37.                 {
  38.                         DBObject ent = operateDwgTrans.GetObject(id, OpenMode.ForWrite);
  39.                        
  40.                         ent.Erase();
  41.                 }
  42.                
  43.                 curDwg.Purge(idsToRemove);
  44.                
  45.                 operateDwgTrans.Commit();
  46.         }
  47.        
  48.         curDwg.SaveAs(dwgName, true, DwgVersion.Current, curDwg.SecurityParameters);
  49.        
  50.         curDwg.Dispose();
  51. }
  52.  

Догадываюсь что не всё так просто, потому что открыть получившиеся чертежи без восстановления нельзя.  :(



Нашел тему где человеку помогли решить подобную проблему с использованием lisp программы, но хотелось бы, если это возможно, использовать .net .

https://adn-cis.org/forum/index.php?topic=7126.msg20784#msg20784

Знаю что мне не хватает теории. Прошу подсказать по мере возможности)

P.S.
Нашел множество готовых Lisp функций которые объединил для выполнения требуемой задачи.
Кому нужно:
https://github.com/AndreyAdamenko/ACADbreakDrawingByLayouts

« Последнее редактирование: 01-03-2021, 12:48:02 от Andrey-Adamenko »

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 822
  • Карма: 166
    • Мои плагины к Автокаду
Используйте LayoutManager.Current.DeleteLayout
Возможно придется еще переключить текущую БД HostApplicationServices.WorkingDatabase (но это не факт)

Оффлайн Andrey-AdamenkoАвтор темы

  • ADN OPEN
  • Сообщений: 13
  • Карма: 0
  • CAD Manager
Используйте LayoutManager.Current.DeleteLayout
Возможно придется еще переключить текущую БД HostApplicationServices.WorkingDatabase (но это не факт)

Похоже это так не работает)

Написал такой код для теста:

Код - C# [Выбрать]
  1. string curDwgName = Utils.SelectFile();
  2.  
  3. if (curDwgName == null) return;
  4.  
  5. Database curDwg = new Database();
  6.  
  7. curDwg.ReadDwgFile(curDwgName, FileOpenMode.OpenForReadAndWriteNoShare, false, null);
  8.  
  9. HostApplicationServices.WorkingDatabase = curDwg;
  10.  
  11. try
  12. {
  13.         LayoutManager.Current.DeleteLayout("1");
  14.        
  15.         LayoutManager.Current.DeleteLayout("2");
  16. }
  17. catch (System.Exception ex)
  18. {
  19.        
  20. }
  21.  

В итоге AutoCAD "фаталит" без всяких объяснений (((

Оффлайн pavka_97

  • ADN OPEN
  • ***
  • Сообщений: 150
  • Карма: 3

Оффлайн Andrey-AdamenkoАвтор темы

  • ADN OPEN
  • Сообщений: 13
  • Карма: 0
  • CAD Manager
Здесь https://adn-cis.org/forum/index.php?topic=2985.msg11417#msg11417 смотрели?

Здравствуйте!

Не натыкался. Спасибо за наводку.

Но метод указанный в решении заставляет AutoCAD аварийно завершить работу после выполнения. Хотя получившийся файл открывается без проблем.

Оффлайн pavka_97

  • ADN OPEN
  • ***
  • Сообщений: 150
  • Карма: 3
Сейчас детально не могу сказать. АН где то писал что использование LayoutManager вносит "подковерные " изменения в базы данных. Я  например ипользовал костыль ввиде запоминания и переключения  баз и листов туда сюда.

Оффлайн Andrey-AdamenkoАвтор темы

  • ADN OPEN
  • Сообщений: 13
  • Карма: 0
  • CAD Manager
Сейчас детально не могу сказать. АН где то писал что использование LayoutManager вносит "подковерные " изменения в базы данных. Я  например ипользовал костыль ввиде запоминания и переключения  баз и листов туда сюда.

Извините, не понял что значит "туда сюда" и для чего это?

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 624
  • Карма: 158
    • ПГСу Бложик
Как то так... корявенько, но вроде не падает
Код - C# [Выбрать]
  1.     /// <summary>
  2.     /// Удаляем лишние листы
  3.     /// </summary>
  4.     /// <param name="db">база данных из которой удаляем листы</param>
  5.     /// <param name="layoutHandle">лист который не нужно удалять</param>
  6.     private static void DeleteLayouts(Db.Database db, Db.Handle layoutHandle)
  7.     {
  8.       Db.Database oldDb = Db.HostApplicationServices.WorkingDatabase;
  9.       Db.HostApplicationServices.WorkingDatabase = db;
  10.  
  11.       //Перед удалением Layout нужно переключиться на тот, который не удаляем
  12.       var layoutManager = Db.LayoutManager.Current;
  13.       Db.ObjectId layoutId = Db.ObjectId.Null;
  14.  
  15.       if (db.TryGetObjectId(layoutHandle, out layoutId))
  16.       {
  17.         if (!layoutId.IsNull)
  18.         {
  19.           if (layoutId.ObjectClass == Rtm.RXObject.GetClass(typeof(Db.Layout)))
  20.           {
  21.             layoutManager.SetCurrentLayoutId(layoutId);
  22.  
  23.             //и удаляем все остальные
  24.             foreach (var layout in CloneDwg.GetLayoutList(db))
  25.             {
  26.               if (layout.Key != layoutHandle)
  27.               {
  28.                 //Так. как CloneDwg.GetLayoutList() возвращает Хандл и
  29.                 //Имя из штампа, то нужно узнать простое имя листа
  30.                 string currentLayoutNmae = "";
  31.                 if (db.TryGetObjectId(layout.Key, out layoutId))
  32.                 {
  33.                   if (layoutId.ObjectClass == Rtm.RXObject.GetClass(typeof(Db.Layout)))
  34.                   {
  35.                     if (!layoutId.IsErased && !layoutId.IsEffectivelyErased)
  36.                     {
  37.                       using (var layoutTemp = layoutId.Open(Db.OpenMode.ForRead) as Db.Layout)
  38.                       {
  39.                         currentLayoutNmae = layoutTemp.LayoutName;
  40.                       }
  41.                       //и тут нужно передать уже простое имя листа
  42.                       try
  43.                       {
  44.                         //Autodesk.AutoCAD.Runtime.Exception: "eDelDoesNotExist"
  45.                         layoutManager.DeleteLayout(currentLayoutNmae);
  46.                       }
  47.                       catch (Exception ex)
  48.                       {
  49.                         LogView.WriteLine($"Err. layout {currentLayoutNmae}, ex: {ex.Message}");
  50.                         //throw;
  51.                       }
  52.                     }
  53.                   }
  54.                 }
  55.               }
  56.             }
  57.           }
  58.         }
  59.       }
  60.       Db.HostApplicationServices.WorkingDatabase = oldDb;
  61.       ////https://adn-cis.org/forum/index.php?topic=7427.0
  62.       ////https://forums.autodesk.com/t5/net/erasing-layouts/m-p/5801221#M45848
  63.     }
  64.  

на всякий случай вот сигнатура метода CloneDwg.GetLayoutList
Код - C# [Выбрать]
  1. public static Dictionary<Db.Handle, string> GetLayoutList(Db.Database db)
в этом методе получается хендл листа и имя из штампа на этом листе.

ИМХО, задача порезать файлы на куски и поименовать эти файлы по именам листова, очень типовая и через это проходят почти все =(
« Последнее редактирование: 11-02-2021, 15:15:09 от Владимир Шу »

Оффлайн Andrey-AdamenkoАвтор темы

  • ADN OPEN
  • Сообщений: 13
  • Карма: 0
  • CAD Manager
Владимир Шу, Спасибо за образец! Но он тоже не работает...

Я так понял что в 2014 каде метод layoutManager.SetCurrentLayoutId отсутствует, заменил его.

Может я что то напутал, вот код:

Код - C# [Выбрать]
  1. [Rtm.CommandMethod("Test")]
  2. public static void Test()
  3. {
  4.         Db.Database db = new Db.Database();
  5.        
  6.         db.ReadDwgFile(@"D:\TestFile.dwg", Db.FileOpenMode.OpenForReadAndWriteNoShare, false, null);
  7.        
  8.         string targetLayName = "2";
  9.        
  10.         Db.Handle targetLayHandle = new Db.Handle();
  11.        
  12.         using (Db.Transaction operateDwgTrans = db.TransactionManager.StartTransaction())
  13.         {
  14.                 Db.DBDictionary spaces = operateDwgTrans.GetObject(db.LayoutDictionaryId, Db.OpenMode.ForWrite) as Db.DBDictionary;
  15.                
  16.                 foreach (Db.DBDictionaryEntry curSpace in spaces)
  17.                 {
  18.                         Db.ObjectId curSpaceId = curSpace.Value;
  19.                        
  20.                         Db.Layout acadLayout = operateDwgTrans.GetObject(curSpaceId, Db.OpenMode.ForWrite) as Db.Layout;
  21.                        
  22.                         if (acadLayout.LayoutName == targetLayName)
  23.                         {
  24.                                 targetLayHandle = acadLayout.Handle;
  25.                         }
  26.                 }
  27.         }
  28.        
  29.         try
  30.         {
  31.                 DeleteLayouts(db, targetLayHandle);
  32.         }
  33.         catch (System.Exception ex)
  34.         {
  35.                 Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog($"Message: {ex.Message}; Trace: {ex.StackTrace}!\n");
  36.         }
  37.        
  38.         try
  39.         {
  40.                 db.SaveAs(@"D:\TestFileNew.dwg", Db.DwgVersion.Current);
  41.         }
  42.         catch (System.Exception ex)
  43.         {
  44.                 Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog($"Message: {ex.Message};\n Trace: {ex.StackTrace}!\n");
  45.         }
  46.        
  47.        
  48.        
  49.         //db.Dispose();
  50. }
  51.  
  52. /// <summary>
  53. /// Удаляем лишние листы
  54. /// </summary>
  55. /// <param name="db">база данных из которой удаляем листы</param>
  56. /// <param name="layoutHandle">лист который не нужно удалять</param>
  57. private static void DeleteLayouts(Db.Database db, Db.Handle layoutHandle)
  58. {
  59.         Db.Database oldDb = Db.HostApplicationServices.WorkingDatabase;
  60.         Db.HostApplicationServices.WorkingDatabase = db;
  61.        
  62.         //Перед удалением Layout нужно переключиться на тот, который не удаляем
  63.         Db.LayoutManager layoutManager = Db.LayoutManager.Current;
  64.         Db.ObjectId layoutId = Db.ObjectId.Null;
  65.        
  66.         string targetLayName = "";
  67.        
  68.         if (db.TryGetObjectId(layoutHandle, out layoutId))
  69.         {
  70.                 if (!layoutId.IsNull)
  71.                 {
  72.                         if (layoutId.ObjectClass == Rtm.RXObject.GetClass(typeof(Db.Layout)))
  73.                         {
  74.                                 using (Db.Transaction operateDwgTrans = db.TransactionManager.StartTransaction())
  75.                                 {
  76.                                         Db.Layout lay = operateDwgTrans.GetObject(layoutId, Db.OpenMode.ForRead) as Db.Layout;
  77.                                        
  78.                                         targetLayName = lay.LayoutName;
  79.                                 }
  80.                                
  81.                                 layoutManager.CurrentLayout = targetLayName;
  82.                                
  83.                                 //и удаляем все остальные
  84.                                 foreach (var layout in CloneDwg.GetLayoutList(db))
  85.                                 {
  86.                                         if (layout.Key != layoutHandle)
  87.                                         {
  88.                                                 //Так. как CloneDwg.GetLayoutList() возвращает Хандл и
  89.                                                 //Имя из штампа, то нужно узнать простое имя листа
  90.                                                 string currentLayoutName = "";
  91.                                                 if (db.TryGetObjectId(layout.Key, out layoutId))
  92.                                                 {
  93.                                                         if (layoutId.ObjectClass == Rtm.RXObject.GetClass(typeof(Db.Layout)))
  94.                                                         {
  95.                                                                 if (!layoutId.IsErased && !layoutId.IsEffectivelyErased)
  96.                                                                 {
  97.                                                                         using (Db.Transaction operateDwgTrans = db.TransactionManager.StartTransaction())
  98.                                                                         {
  99.                                                                                 Db.Layout acadLayout = operateDwgTrans.GetObject(layoutId, Db.OpenMode.ForWrite) as Db.Layout;
  100.                                                                                
  101.                                                                                 currentLayoutName = acadLayout.LayoutName;
  102.                                                                         }
  103.                                                                        
  104.                                                                         //и тут нужно передать уже простое имя листа
  105.                                                                         try
  106.                                                                         {
  107.                                                                                 //Autodesk.AutoCAD.Runtime.Exception: "eDelDoesNotExist"
  108.                                                                                 layoutManager.DeleteLayout(currentLayoutName);
  109.                                                                         }
  110.                                                                         catch (Exception ex)
  111.                                                                         {
  112.                                                                                 //LogView.WriteLine($"Err. layout {currentLayoutNmae}, ex: {ex.Message}");
  113.                                                                                 //throw;
  114.                                                                                
  115.                                                                                 Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog($"Message: {ex.Message};\n Trace: {ex.StackTrace}!\n");
  116.                                                                         }
  117.                                                                 }
  118.                                                         }
  119.                                                 }
  120.                                         }
  121.                                 }
  122.                         }
  123.                 }
  124.         }
  125.         Db.HostApplicationServices.WorkingDatabase = oldDb;
  126. }
  127.  
  128. internal class CloneDwg
  129. {
  130.         public static Dictionary<Db.Handle, string> GetLayoutList(Db.Database db)
  131.         {
  132.                 Dictionary<Db.Handle, string> result = new Dictionary<Db.Handle, string>();
  133.                
  134.                 using (Db.Transaction operateDwgTrans = db.TransactionManager.StartTransaction())
  135.                 {
  136.                         Db.DBDictionary spaces = operateDwgTrans.GetObject(db.LayoutDictionaryId, Db.OpenMode.ForWrite) as Db.DBDictionary;
  137.                        
  138.                         foreach (Db.DBDictionaryEntry curSpace in spaces)
  139.                         {
  140.                                 Db.ObjectId curSpaceId = curSpace.Value;
  141.                                
  142.                                 Db.Layout acadLayout = operateDwgTrans.GetObject(curSpaceId, Db.OpenMode.ForWrite) as Db.Layout;
  143.                                
  144.                                 if (acadLayout.LayoutName == "Model") continue;
  145.                                
  146.                                 result.Add(acadLayout.Handle, acadLayout.LayoutName);
  147.                         }
  148.                 }
  149.                                
  150.                 return result;
  151.         }
  152. }      
  153.  

При выполнении db.SaveAs() (строка 40) появляется Exception (eBrokenHandle), описание не нашел:

« Последнее редактирование: 15-02-2021, 10:17:55 от Andrey-Adamenko »

Оффлайн Andrey-AdamenkoАвтор темы

  • ADN OPEN
  • Сообщений: 13
  • Карма: 0
  • CAD Manager
Спасибо всем за участие!

Не смог сделать то что мне нужно на .net, но нашел кучу готовых функций на Lisp.
Благодаря этому получилось очень даже не плохо:
https://github.com/AndreyAdamenko/ACADbreakDrawingByLayouts

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