Разрезать Region

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

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

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #60 : 04-06-2014, 11:51:24 »
Если мы взрываем объединённые регионы, то при первом взрыве мы получаем набор отдельных регионов,
Ну это просто сказачное везение - тогда проблемма буквы Ш решаеться 5-тью строками, но предложенная мной оптимизация идет лесом - т.к. "островок" может попастся внутри клетки и пересечения не будет - то есть каждую клетку надо "булеанить", а потом еще и проверить brepom - в общем процесс будет совсем не быстрый...
В общем реально только если клетки не такие маленькие как в примере (т.к. все равно будут взрываться).

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #61 : 04-06-2014, 11:52:17 »
bargool,
Спасибо, посмотрю обязательно.

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #62 : 04-06-2014, 11:54:42 »
то есть каждую клетку надо "булеанить", а потом еще и проверить brepom - в общем процесс будет совсем не быстрый...
Именно так я и делаю. Пользователя скорость устраивает, по его заявлению, т.к. написанный им вариант на VBA работал ещё медленнее.

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #63 : 04-06-2014, 12:16:08 »
Перед тем как делать ниженаписанное вначале надо ответить на главный вопрос "Надо-ли":
Чтоб не "булеанить" каждую клетку, можно brep'ом "собрать" все точки кривых region'а, одновременно индексируя их по координатам будующих клеткок (точнее пометить клетки в которых есть хотя-бы одна точка). Далее все непроиндексированные клетки рисуются как есть, а "попавшие под индекс" булеаняться и рекурсивно взрываются (или там 1-го взрыва всегда достаточно?) , для проверки клетки "внутри\снаружи" применять тот-же алгоритм, что и с пересечением.

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #64 : 04-06-2014, 12:21:00 »
или там 1-го взрыва всегда достаточно?
Цитата: Андрей Бушман
2. Если мы взрываем объединённые регионы, то при первом взрыве мы получаем набор отдельных регионов, с сохранением отверстий (если таковые имеются). Повторный взрыв делает то же самое, что и в п.1.
рекурсивный взрыв не требуется, всё "как надо" после первого взрыва.

Перед тем как делать ниженаписанное вначале надо ответить на главный вопрос "Надо-ли"
На данный момент не надо.

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

  • Administrator
  • *****
  • Сообщений: 13877
  • Карма: 1785
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #65 : 04-06-2014, 17:29:17 »
Т.о. если я вижу, что Face Complexes более 1, то взрываю регион и полученные новые регионы добавляю в набор. Проверил, работает.
Отлично. Если это всегда так, то совсем замечательно. Можно например написать свой вариант команды EXPLODE, которая расчленяет только Region/Solid3d, но только тогда, когда они состоят из отдельных частей и расчленяются на составляющие Region/Solid3d.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #66 : 07-06-2014, 14:12:00 »
Я тут приболел, но если в голове есть алгоритм (да еще и понимание, что его реализация лежит в весьма ограниченном числе строк), то для хорошего человека его и реализовать не грех - и вот когда появились "умственные просветы"... В общем реализация вышеописанного алгоритма оказалась даже меньше чем я ожидал (тем паче что практически весь brep я скопипастил в соседней теме у А. Ривилиса - думаю он не против :) ) - и почуствовав, что как-то больно быстро получилось - еще и перетащил функцию в команду:
Код - C# [Выбрать]
  1. open Autodesk.AutoCAD.ApplicationServices
  2. open Autodesk.AutoCAD.Runtime
  3. open Autodesk.AutoCAD.DatabaseServices
  4. open Autodesk.AutoCAD.Geometry
  5. open Autodesk.AutoCAD.EditorInput
  6. open Autodesk.AutoCAD.BoundaryRepresentation
  7.  
  8. let Init()=
  9.   let doc=Application.DocumentManager.MdiActiveDocument
  10.   doc,doc.Editor,doc.Database,doc.TransactionManager.StartTransaction
  11.  
  12. let RegExplode (rg:Region)=
  13.   let brep=new Brep(rg)
  14.   seq{for f in brep.Faces->f}|>function
  15.     |faces when faces|>Seq.skip 1|>Seq.isEmpty->[rg]
  16.     |_->let coll=new DBObjectCollection()
  17.         rg.Explode(coll)
  18.         rg.Dispose()
  19.         [for x in coll->x:?>Region]
  20.  
  21. let IndexRegion (rg:Region) width height=
  22.   let deltaX=width/10.0
  23.   let deltaY=height/10.0
  24.   let x0,y0=let pt=rg.GeometricExtents.MinPoint
  25.             pt.X,pt.Y
  26.   use br=new Brep(rg)
  27.   seq{for face in br.Faces->
  28.        seq{for lop in face.Loops->
  29.             seq{for edge in lop.Edges->
  30.                  use curve=edge.Curve
  31.                  let intr=curve.GetInterval()
  32.                  let p1,p2=intr.LowerBound,intr.UpperBound          
  33.                  {p1..(p2-p1)/(curve.GetLength(p1,p2,(min deltaX deltaY)/2.0)/(min deltaX deltaY))..p2}
  34.                    |>Seq.map (curve.EvaluatePoint)|>Seq.toArray}}}
  35.                    |>Seq.concat|>Seq.concat|>Seq.concat //seq point3d
  36.     |>Seq.fold (fun set pt->let x,y=(pt.X-x0)/width,(pt.Y-y0)/height
  37.                             set|>Set.add(x|>int,y|>int)
  38.                                |>Set.add(x|>int,y+0.1|>int)
  39.                                |>Set.add(x|>int,y-0.1|>int)
  40.                                |>Set.add(x+0.1|>int,y|>int)
  41.                                |>Set.add(x+0.1|>int,y+0.1|>int)
  42.                                |>Set.add(x+0.1|>int,y-0.1|>int)
  43.                                |>Set.add(x-0.1|>int,y|>int)
  44.                                |>Set.add(x-0.1|>int,y+0.1|>int)
  45.                                |>Set.add(x-0.1|>int,y-0.1|>int))
  46.                (Set [])
  47.  
  48. let MakeRegion (pt1:Point3d) width height=
  49.   let cl=new DBObjectCollection()
  50.   new Polyline2d(Poly2dType.SimplePoly,new Point3dCollection([|pt1
  51.                                                                new Point3d(pt1.X+width,pt1.Y,pt1.Z)
  52.                                                                new Point3d(pt1.X+width,pt1.Y+height,pt1.Z)
  53.                                                                new Point3d(pt1.X,pt1.Y+height,pt1.Z)|]),
  54.                  0.0,true,0.0,0.0,new DoubleCollection([|0.0;0.0;0.0;0.0|]))|>cl.Add|>ignore
  55.   Region.CreateFromCurves(cl).[0]:?>Region
  56.  
  57. [<CommandMethod "Narez">]
  58. let Narez()=
  59.   let doc,ed,db,trf=Init()
  60.   let Slice width height id=
  61.     let start=System.DateTime.Now
  62.     use tr=trf()
  63.     let rg=tr.GetObject(id,OpenMode.ForWrite):?>Region
  64.     let ext=rg.GeometricExtents
  65.     let block=tr.GetObject(rg.BlockId,OpenMode.ForWrite):?>BlockTableRecord
  66.     let AppendEnt (ent:Entity)=
  67.         block.AppendEntity ent|>ignore
  68.         tr.AddNewlyCreatedDBObject(ent,true)    
  69.     let obr=MakeRegion ext.MinPoint width height
  70.     let mtw=Matrix3d.Displacement(new Vector3d(width,0.0,0.0))
  71.     let mth=Matrix3d.Displacement(new Vector3d(-width*(((ext.MaxPoint.X-ext.MinPoint.X)/width|>int)+1|>float),height,0.0))
  72.     let index=IndexRegion rg width height
  73.     "\n"+string(System.DateTime.Now-start)+" - индексация выполненна; "|>ed.WriteMessage
  74.     {0..(ext.MaxPoint.Y-ext.MinPoint.Y)/height|>int}
  75.       |>Seq.iter (fun y ->{0..(ext.MaxPoint.X-ext.MinPoint.X)/width|>int}
  76.                              |>Seq.fold (fun (next,check) x ->
  77.                                 (Set.contains (x,y) index,next,check)|>function
  78.                                    |true,_,_->let cl=(obr.Clone():?>Region)
  79.                                               obr.TransformBy(mtw)
  80.                                               cl.BooleanOperation(BooleanOperationType.BoolIntersect,rg.Clone():?>Region)
  81.                                               if cl.Area<0.0001
  82.                                                  then cl.Dispose()
  83.                                                  else cl|>RegExplode|>Seq.iter AppendEnt
  84.                                               false,true
  85.                                    |_,_,true->let cl=(obr.Clone():?>Region)
  86.                                               obr.TransformBy(mtw)
  87.                                               cl.BooleanOperation(BooleanOperationType.BoolIntersect,rg.Clone():?>Region)
  88.                                               if cl.Area<0.0001
  89.                                                  then cl.Dispose()
  90.                                                       false,false
  91.                                                  else cl|>RegExplode|>Seq.iter AppendEnt
  92.                                                       true,false
  93.                                    |_,true,_->let cl=(obr.Clone():?>Region)
  94.                                               obr.TransformBy(mtw)
  95.                                               AppendEnt cl
  96.                                               true,false
  97.                                    |_->obr.TransformBy(mtw)
  98.                                        false,false) (false,false)|>ignore
  99.                           obr.TransformBy(mth))
  100.     obr.Dispose()
  101.     tr.Commit()
  102.     "всего затраченно: "+string(System.DateTime.Now-start)+"\n"|>ed.WriteMessage
  103.   let inline (|PromptOk|_|) (pr:^a)=(^a:(member Status:PromptStatus)pr)=PromptStatus.OK|>function
  104.                                       |true->Some(pr)
  105.                                       |false->None
  106.   let ErrInput()=ed.WriteMessage("\nКоманда отменена")
  107.   ed.GetSelection(new SelectionFilter([|new TypedValue(int(DxfCode.Start),"REGION")|]))|>function
  108.     |PromptOk(ss)->ed.GetDistance("\nШирина: ")|>function
  109.       |PromptOk(width)->ed.GetDistance("\nВысота:")|>function
  110.         |PromptOk(height)->let start=System.DateTime.Now
  111.                            seq{for ssid in ss.Value->ssid.ObjectId}
  112.                             |>Seq.iter (Slice width.Value height.Value)
  113.                            "\nОбщее время: "+string(System.DateTime.Now-start)|>ed.WriteMessage
  114.         |_->ErrInput()
  115.       |_->ErrInput()
  116.     |_->ErrInput()
Имя то-же - narez, в итоге получилось побыстрее чем предыдущая версия (без стойкости к букве Ш).
т.к. написанный им вариант на VBA работал ещё медленнее.
Уповать на скорость API против алгоритма никогда не стоит - рано или поздно тыкнут носом - вот мол на такой ...не пишут, а работает в Х-раз быстрее, а ты тут извращаешся, а результату...

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

  • Administrator
  • *****
  • Сообщений: 13877
  • Карма: 1785
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #67 : 08-06-2014, 01:40:07 »
Дима. Интересно насколько изменится скорость если использовать не edge.Curve, а edge.Curve.NativeCurve, как я использовал здесь.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #68 : 08-06-2014, 02:38:04 »
Маэстро спасибо за дружеский анализ. Я откровенно говоря отношусь скептически к "битве за урожай", мне например, гораздо больше смущают в моем-же коде строки 37-47, эту "самокопипасту", чуть что исправлять замучаешься - а завернуть во что-то более приемлемое глазу ее конечно стоит - ну ладно болею - простят. По существу вопроса т.к. по крайней мере на "экземпляре" Андрея, индексация занимат не значительную часть времени, то и приросту изменения даст не много - даже если время индексации обнулиться совсем - по факту на первом прогоне, Ваш вариант получился даже медленней - хотя я считаю, что это погрешность измерений - но по моему твердому убеждению, все что лежит в ее пределах - принебрегаем и выбираем более надежный, либо читабельный. Вот логи (Ваш вариант NAREZNAT запускался вторым после отмены работы первого), я не считал отдельно индексацию - но там ИХМО +- одинаково (значительный отрыв результата первого региона грешу на подгрузку и пр. предварительную работу).

з.ы. Я в этом коде пренебрег подсчетом нулевой точки - последовательность можно преобразовать в массив (например), расчитать минимальные X и Y и "плясать" от них (то есть преобразовать эту-же последовательность), а не от GetBoundingBox как в примере, но т.к. индескации в этих "пустых" клетках нет, то по ней просто пробежиться TransformBy - даже не создавая клон, и тем паче не анализируя его - в общем проверять даст ли это выйгрыш мне просто лень, но вполне возможно на элипсах процентов 5 там прячется от общей производительности. Я не люблю хранить большие массивы данных без особой на то необходимости (хотя преобразование последовательности в массив по каждому контуру таки есть - т.к. он должен быть Dispose'ен - инчае вылезит фатальный привет на следующем вызове) - а считать их два раза в данном случае вобще глупо - точно "прироста" не даст - а скорее наоборот.
« Последнее редактирование: 08-06-2014, 03:08:26 от Дима_ »

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

  • Administrator
  • *****
  • Сообщений: 13877
  • Карма: 1785
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Разрезать Region
« Ответ #69 : 08-06-2014, 02:52:19 »
Понятно. Я не оценил тот факт, что вклад определения BoundingBox в общий алгоритм мизерный. Тем более, что часть регионов вообще не имеют сплайновых образующих.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #70 : 09-06-2014, 10:40:29 »
Порой твои записи меня удивляют...
Уповать на скорость API против алгоритма никогда не стоит
Я никогда и не полагаюсь на это. Похоже, что ты меня неверно понял.
Цитата: Андрей Бушман
Пользователя скорость устраивает, по его заявлению, т.к. написанный им вариант на VBA работал ещё медленнее.
Эта фраза означает, что пользователь предварительно написал свой вариант реализации, используя для этого язык VBA и конечный результат работал медленней моего. Язык программирования, используемый пользователем, был мною указан лишь в качестве дополнительной информации. Я не вкладывал в это предложение смысл, мол "у юзера медленней, потому что он пишет на VBA". Алгоритм реализации у пользователя был свой: половинить до тех пор, пока очередная половина не станет меньше либо равной обозначенным dx и dy.

Цитата: Дима_
в итоге получилось побыстрее чем предыдущая версия (без стойкости к букве Ш).
Как я уже писал выше корректная обработка "Ш" и ей подобных ситуаций - обязательное требование проектировщиков. В виду этого выбираю алгоритм менее быстрый, но дающий именно те результаты, которые от него ожидают во всех (а не "почти во всех") возможных ситуациях (во всяком случае в тех, которые я использовал в ходе тестирования).
Цитата: Дима_
Я тут приболел
Ты это дело брось! :)

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #71 : 09-06-2014, 11:13:48 »
Как я уже писал выше корректная обработка "Ш" и ей подобных ситуаций
Вобщем опять не там запятая - этот как раз и устойчивый к букве Ш (собсвеннно из-за чего и пришлось проверку пересечения заменить индексацией) - то есть корректно обработает.

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #72 : 09-06-2014, 13:11:31 »
Дима_, первое, что мне бросилось в глаза - это орда записей:
Цитата: IDE Output in Debug mode
Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.Region): DisposableWrapper
Поскольку в твоём варианте нет возможности указать значение delta, то мне не удаётся проверить твой код на тех же условиях, когда вариант А.Н. выдавал исключение для delta = 0.1.

Цитата: Дима_
Вобщем опять не там запятая - этот как раз и устойчивый к букве Ш (собсвеннно из-за чего и пришлось проверку пересечения заменить индексацией) - то есть корректно обработает.
Да, "Ш" обрабатывается корректно и в целом всё работает весьма шустро. Я через ILSpy конвертну твой код в C#, дабы посмотреть алгоритм.
« Последнее редактирование: 09-06-2014, 13:21:48 от Андрей Бушман »

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Re: Разрезать Region
« Ответ #73 : 09-06-2014, 13:28:45 »
С диспосами посмотрю - по коду не вижу (достал меня этот автокад - угадывай где надо, где нет). Delta проверки =1/10 = меньшего шага, + если ближе 1/10 к краю индексирует и соседнеий сегмент.

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Re: Разрезать Region
« Ответ #74 : 09-06-2014, 13:29:03 »
Не... результат декомпиляции ILSpy таков, что это мне будет нужно не один день потратить, чтобы понять, что есть что (море кракозяблов в наименованиях классов, непонятная иерархия и т.п.). Оставляю всё как есть.