Разрезать Region

Автор Тема: Разрезать Region  (Прочитано 37784 раз)

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

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Разрезать Region
« : 26-05-2014, 14:08:29 »
Необходимо некоторый region нарезать сеткой с заданным шагом по оси X (dx) и по оси Y (dy). На выходе должны получиться новые объекты region.

На данный момент программно делаю так:
1. Выдавливаю исходный Region в Solid3D.
2. Нарезаю полученный Solid3d сеткой (по оси X и по оси Y). На выходе - огромное количество "ломтиков" (новых объектов Solid3d).
3. Выполняю Explode для каждого Solid3d и удаляю все полученные объекты Region, кроме того, который лежит в основании.

Примечание: Все промежуточные Solid3d инициализирую в блоке using, тем самым автоматически вызывая для них Dispose, т.к. не добавляю их в Database. Добавляю в Database только новые Region.

Хотелось бы выполнять операцию более быстро, минуя промежуточные шаги с использованием Solid3D. Есть ли более быстрый способ сделать это средствами API AutoCAD?

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #1 : 26-05-2014, 14:31:02 »
Хотелось бы выполнять операцию более быстро, минуя промежуточные шаги с использованием Solid3D. Есть ли более быстрый способ сделать это средствами API AutoCAD?
У класса Region есть метод BooleanOperation, который теоретически может помочь. Т.е. можно создать эталонный Region размером [dx dy] и, сдвигая его по сетке в границах исходного региона, выполнить пересечение (BoolIntersect) исходного Region с эталонным.
Не уверен, что этот способ будет более быстрым, но попробовать можешь.
Тут возможна еще оптимизация при помощи Brep, если границы эталонного Region находятся внутри исходного Region и не пересекают его, то результирующий Region равен эталонному, а если пересечения границ нет вообще, то эталонный Region вне исходного.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн German

  • ADN Club
  • **
  • Сообщений: 84
  • Карма: 13
Re: Разрезать Region
« Ответ #2 : 26-05-2014, 15:19:01 »
Т.е. можно создать эталонный Region размером [dx dy] и, сдвигая его по сетке в границах исходного региона
Может не сдвигать, а сразу прямоугольный массив создать?

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #3 : 26-05-2014, 15:24:36 »
Может не сдвигать, а сразу прямоугольный массив создать?
Думаю что тут будут большие расходы по памяти если достаточно большой исходный регион.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #4 : 26-05-2014, 18:05:07 »
Не уверен, что этот способ будет более быстрым, но попробовать можешь.
Я думаю производительность алгоритмов в данном случае будет зависеть от исходных данных (то есть будут случаи когда выигрывает один на одних исходных данных, и проигрывает на других) - в общем смотреть что ближе к "среднестатистическому использованию".
з.ы. но в общем да способ "кривоватый"...

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #5 : 27-05-2014, 22:37:46 »
У класса Region есть метод BooleanOperation, который теоретически может помочь. Т.е. можно создать эталонный Region размером [dx dy] и, сдвигая его по сетке в границах исходного региона, выполнить пересечение (BoolIntersect) исходного Region с эталонным.
Не уверен, что этот способ будет более быстрым, но попробовать можешь.
Так и сделал. Прироста производительности по сравнению с этим не наблюдаю (примерно то на то и выходит по времени), однако есть и положительный момент: корректно обрабатываются регионы, выполненные сплайнами.

Цитировать
Тут возможна еще оптимизация при помощи Brep, если границы эталонного Region находятся внутри исходного Region и не пересекают его, то результирующий Region равен эталонному, а если пересечения границ нет вообще, то эталонный Region вне исходного.
Завтра добавлю эту оптимизацию. Надеюсь, что после этого скорость работы всё же увеличится.

В качестве дополнительной оптимизации попробую распараллелить все манипуляции с регионами по разным потокам. Исхожу из того, что весь процесс происходит без привлечения Database и Application; зачистку промежуточных результатов выполняю самостоятельно посредством вызова Dispose на экземплярах классов AutoCAD .NET API. Не факт, что AutoCAD не умрёт после этого, но попробовать стоит, имхо...

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #6 : 28-05-2014, 00:13:54 »
В качестве дополнительной оптимизации попробую распараллелить все манипуляции с регионами по разным потокам. Исхожу из того, что весь процесс происходит без привлечения Database и Application;
Так как все объекты из пространства имён Autodesk.AutoCAD.DatabaseServices есть ни что иное как обертки над ObjectARX классами AcDb из библиотеки acdbxx.dll, которая в свою очередь не является потокобезопасной, я бы поостерегся это делать. Во всяком случае рекомендую оставить этот вид оптимизации на потом.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #7 : 28-05-2014, 11:09:55 »
обертки над ObjectARX классами AcDb из библиотеки acdbxx.dll, которая в свою очередь не является потокобезопасной, я бы поостерегся это делать.
Ок, спасибо, пожалуй откажусь от этой идеи.

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #8 : 28-05-2014, 23:12:45 »
Цитировать
    Тут возможна еще оптимизация при помощи Brep, если границы эталонного Region находятся внутри исходного Region и не пересекают его, то результирующий Region равен эталонному, а если пересечения границ нет вообще, то эталонный Region вне исходного.

Завтра добавлю эту оптимизацию. Надеюсь, что после этого скорость работы всё же увеличится.
Как оказалось, данная оптимизация оказалась невозможной, т.к. в ряде случаев приводит к некорректным результатам проверки.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #9 : 30-05-2014, 13:16:11 »
В ADN DevHelp подтвердили баг метода BrepEntity.GetMassProperties, т.е. с его помощью в AutoCAD .NET API получать Centroid для Region и Solid3d нельзя.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #10 : 30-05-2014, 13:24:01 »
В ADN DevHelp подтвердили баг
А ведь помнится вы утверждали, что в Autodesk тестируют код... Ну и как же они его тестируют, когда обозначенный функционал не работает ни в 2009-м, ни в 2015-м? Что-то мне подсказывает, что все промежуточные версии покажут тот же результат.

Практика показывает, что тестирование компании Autodesk очень напоминает способ "скомпилировалось, значит всё в порядке". Первая же проверка обозначенного функционала сразу бы выявила его неработоспособность. Отсюда вывод: этот свой код в Autodesk даже и не пытались тестировать.

Видимо у нас с вами разные понятия о тестировании...

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #11 : 30-05-2014, 13:27:26 »
А ведь помнится вы утверждали, что в Autodesk тестируют код...
Это у тебя склероз проявляется. ;) Я утверждал, что тестируют сам AutoCAD, но не код. Код тестируется (как я понимаю) тот, который они сами используют. Так вот им видимо в голову не пришло использовать managed код для BREP - только native. IMHO.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #12 : 30-05-2014, 13:36:00 »
Код тестируется (как я понимаю) тот, который они сами используют.
Тогда удивляться не приходится.

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #13 : 31-05-2014, 13:32:57 »
Как не странно, паралельно решал схожую проблему (не совсем ту - но тоже с region'ами), есть вариант оптимизации - нужно, как и предлагал А. Ривилис, "гнать" прямоугольник, но заниматься Intersect'ом не на каждой итерации - а анализируя геометрию прохождения границ (строки 55-65): вот "нарезал" из своих кусков:
Код - C# [Выбрать]
  1. open Autodesk.AutoCAD.ApplicationServices
  2. open Autodesk.AutoCAD.Runtime
  3. open Autodesk.AutoCAD.DatabaseServices
  4. open Autodesk.AutoCAD.Geometry
  5.  
  6. let (|AcEnt|AcNum|AcUnknow|) (x:TypedValue)=
  7.   enum<LispDataType>(x.TypeCode|>int)|>function
  8.     |LispDataType.ObjectId->AcEnt(x.Value:?>ObjectId)
  9.     |LispDataType.Int16->AcNum(x.Value:?>int16|>float)
  10.     |LispDataType.Int32->AcNum(x.Value:?>int|>float)
  11.     |LispDataType.Double->AcNum(x.Value:?>float)
  12.     |_->AcUnknow
  13.  
  14. let Rb=function
  15.   |null->[]
  16.   |(rb:ResultBuffer)->rb.AsArray()|>Array.toList
  17.  
  18. let Init()=
  19.   let doc=Application.DocumentManager.MdiActiveDocument
  20.   doc,doc.Editor,doc.Database,doc.TransactionManager.StartTransaction
  21.  
  22. [<LispFunction "NAREZ">]
  23. let Narez arg=
  24.   let doc,ed,db,trf=Init()
  25.   let MakeRegion (pt1:Point3d) width height=
  26.     let cl=new DBObjectCollection()
  27.     new Polyline2d(Poly2dType.SimplePoly,new Point3dCollection([|pt1
  28.                                                                  new Point3d(pt1.X+width,pt1.Y,pt1.Z)
  29.                                                                  new Point3d(pt1.X+width,pt1.Y+height,pt1.Z)
  30.                                                                  new Point3d(pt1.X,pt1.Y+height,pt1.Z)|]),
  31.                    0.0,true,0.0,0.0,new DoubleCollection([|0.0;0.0;0.0;0.0|]))|>cl.Add|>ignore
  32.     Region.CreateFromCurves(cl).[0]:?>Region
  33.   arg|>Rb|>function
  34.     |[AcEnt id;AcNum width;AcNum height]->
  35.             let start=System.DateTime.Now
  36.             use tr=trf()
  37.             tr.GetObject(id,OpenMode.ForWrite)|>function
  38.             | :? Region as rg->let ext=rg.GeometricExtents
  39.                                let block=tr.GetObject(rg.BlockId,OpenMode.ForWrite):?>BlockTableRecord
  40.                                let AppendEnt ent=
  41.                                    block.AppendEntity ent|>ignore
  42.                                    tr.AddNewlyCreatedDBObject(ent,true)
  43.                                let obr=MakeRegion ext.MinPoint width height
  44.                                let mtw=Matrix3d.Displacement(new Vector3d(width,0.0,0.0))
  45.                                let mth=Matrix3d.Displacement(new Vector3d(-width*(((ext.MaxPoint.X-ext.MinPoint.X)/width|>int)+1|>float),height,0.0))
  46.                                {0..(ext.MaxPoint.Y-ext.MinPoint.Y)/height|>int}
  47.                                     |>Seq.iter (fun _ ->{0..(ext.MaxPoint.X-ext.MinPoint.X)/width|>int}
  48.                                                                 |>Seq.fold (fun (next,check) _ ->
  49.                                                                                 let cl=(obr.Clone():?>Region)
  50.                                                                                 obr.TransformBy(mtw)
  51.                                                                                 let pc=new Point3dCollection()
  52.                                                                                 cl.IntersectWith(rg,Intersect.ExtendThis,pc,0,0)
  53.                                                                                 let inters()=cl.BooleanOperation(BooleanOperationType.BoolIntersect,rg.Clone():?>Region)
  54.                                                                                              cl.Area<0.0000001
  55.                                                                                 (pc.Count>0,next,check)|>function
  56.                                                                                   |true,_,_ when inters()->cl.Dispose()
  57.                                                                                                            (false,true)
  58.                                                                                   |true,_,_->AppendEnt cl
  59.                                                                                              (false,true)
  60.                                                                                   |_,_,true when inters()->cl.Dispose()
  61.                                                                                                            (false,false)
  62.                                                                                   |_,_,true|_,true,_->AppendEnt cl
  63.                                                                                                       (true,false)
  64.                                                                                   |_->cl.Dispose()
  65.                                                                                       (false,false))
  66.                                                                            (false,false)|>ignore
  67.                                                         obr.TransformBy(mth))
  68.                                obr.Dispose()
  69.             |_->ed.WriteMessage("Работаем только с Region'ами...\n")
  70.             tr.Commit()
  71.             "Затраченное время: "+string(System.DateTime.Now-start)+"\n"|>ed.WriteMessage
  72.     |_->ed.WriteMessage("Неверный тип аргументов - пример (narez (entlast) 10 10)\n")
Реализованна как лисп функция формат вызова (narez <ename> ширина_сегмента высота_сегмента); проверь идет ли с твоими тестовыми образцами и быстрее-ли.
Скомпилированную для 2014 прикладываю.
p.s. исправил код (стр. 68) - но не вложение.
p.p.s. вложение исправил - но только под 2010.
« Последнее редактирование: 03-06-2014, 12:37:37 от Дима_ »

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #14 : 02-06-2014, 14:14:35 »
Цитировать
вот "нарезал" из своих кусков:
Спасибо, правда для меня F# как китайская азбука.

Цитировать
Реализованна как лисп функция формат вызова (narez <ename> ширина_сегмента высота_сегмента); проверь идет ли с твоими тестовыми образцами и быстрее-ли.
Скомпилированную для 2014 прикладываю.
Очень неудобны следующие моменты:
1. Ctrl + Z отменяет не только то, что выполнила lisp-функция narez. Т.е. по хорошему, в коде должна быть выполнена группировка операций, которые будут отменяться по Ctrl + z, не захватывая при этом лишнего.
2. Я использовал (narez (car(entsel)) 10 10) в тестах. Очень неудобно, что выбирать приходится по одному. Попробовал так: (narez (ssget) 10 10), но не работает. Поскольку мои познания в LISP активно стремятся к нулю, то вполне возможно, что выбрать несколько можно, да я не знаю как - в этом случае прошу показать пример.

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

Спасибо.