Найти область вокруг указанного объекта

Автор Тема: Найти область вокруг указанного объекта  (Прочитано 14338 раз)

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

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

  • ADN Club
  • **
  • Сообщений: 78
  • Карма: 2
2 Александр Пекшев aka Modis
На тот случай, если алгоритм не сможет найти то, что нужно или найти хоть что-нибудь, он сообщит, что перед ним "хрень бесполезная" и предложит пользователю выбрать все данные вручную последовательно, что уже реализовано. :)
2 Boxa.Shu
Спасибо, попробую.
2 trir
Почитал. Круто! Видел нечто подобное реализованное на Lisp. И даже использовал модуль на Lisp для конвертации начерченных вручную таблиц в ATable for AutoCAD.
Оставлю как запасной вариант, если не прокатит вариант Boxa.Shu

СПАСИБО!

Оффлайн Алексей (IdeaSoft)

  • ADN
  • *
  • Сообщений: 1189
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Написал код, с помощью которого можно выбрать отдельно отметки и отдельно расстояния
А что тексты длин и отметок не определенных слоях что ли?
Для автоматизации лучше все хранить в определенных слоях. Тогда и проблем не будет.
Если на слои не ориентироваться то можно просто выбирать объекты геометрически по прямоугольнику
В чем проблема. Даже если профиль длинный. Что пользователю трудно указать две пары точек ограничивающий прямоугольников.
а алгоритм уже выберет тексты из этих двух прямоугольных областей.


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

  • ADN Club
  • **
  • Сообщений: 78
  • Карма: 2
Профили поступают к нам в организацию от разных изыскателей. Предсказать, как именно он будет выглядеть - невозможно. У каждого свои тараканы. И хотя изыскателям выставлены требования к оформлению и даже требовали оформлять изыскания в определенной программе и формате, все равно выдают так, как умеют. При этом, как обычно бывает, выдают в последний момент, когда времени на изменения уже нет. Приходится брать то, что есть.
В итоге, наши линейщики, работающие в GeoSeries сталкиваются с дилеммой, отказаться от GeoSeries и прокладывать трубопровод вручную, или переводить данные профиля в GeoSeries и прокладывать трубопровод с его помощью. Второе, конечно, предпочтительнее, но если профиль слишком длинный, то рискуем все время потратить на подготовку исходных данных для GeoSeries. Вот и попросили помочь.
Значения в ячейках на разные слои не разнесены. Профиль бывает такой длинный, что искушенные пользователи ленятся выбирать его целиком. Выбирают частями. В общем, муторно все это, вот и решил попробовать написать интеллектуальный механизм. В конце концов, описанные выше вычисления современные компьютеры должны выполнять в доли секунд (це ж не 286).

В общем, сделал вот как:
Код - C# [Выбрать]
  1.         private Extents3d GetExtents(Transaction tr, ObjectId[] ids)
  2.         {
  3.             var ext = new Extents3d();
  4.             foreach (var id in ids)
  5.             {
  6.                 var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
  7.                 if (ent != null)
  8.                 {
  9.                     ext.AddExtents(ent.GeometricExtents);
  10.                 }
  11.             }
  12.             return ext;
  13.         }
  14.  
  15.         private bool compareWithPatterns(string value, string[] patterns)
  16.         {
  17.             foreach (string p in patterns)
  18.             {
  19.                 if (value.Contains(p))
  20.                     return true;
  21.             }
  22.             return false;
  23.         }
  24.  
  25.         private ObjectId FindTitle(Transaction tr, ObjectId[] ids, string[] patterns)
  26.         {            
  27.             foreach (ObjectId id in ids)
  28.             {
  29.                 DBObject ent = tr.GetObject(id, OpenMode.ForRead);
  30.                 if (ent is DBText)
  31.                 {
  32.                     DBText text = ent as DBText;
  33.  
  34.                     if (this.compareWithPatterns(text.TextString, patterns))
  35.                         return id;                    
  36.                 }
  37.                 else if (ent is MText)
  38.                 {
  39.                     MText mtext = ent as MText;
  40.  
  41.                     if (this.compareWithPatterns(mtext.Text, patterns))
  42.                         return id;
  43.                 }
  44.             }
  45.  
  46.             return ObjectId.Null;
  47.         }
  48.  
  49.         private void FindDataContour(Editor ed, Transaction tr, ObjectId titleId, Extents3d selectionExtents, out Extents3d extents)
  50.         {
  51.             //get extents of title
  52.             DBObject ent = tr.GetObject(titleId, OpenMode.ForRead);
  53.             if (!ent.Bounds.HasValue)
  54.                 throw new System.Exception("Невозможно определить границы наименования");
  55.  
  56.             Extents3d titleExtents = ent.Bounds.Value;
  57.  
  58.             //get boundary of title
  59.             DBObjectCollection objs = ed.TraceBoundary(titleExtents.MinPoint + (titleExtents.MaxPoint - titleExtents.MinPoint) / 2, true);
  60.  
  61.             if (objs.Count < 2)
  62.                 throw new System.Exception("Контур ячейки с наименованием не найден");
  63.            
  64.             Extents3d titleContourExtents = (objs[objs.Count - 1] as Entity).Bounds.Value;
  65.  
  66.             //calculate data extents
  67.             extents = new Extents3d(
  68.                 titleContourExtents.MinPoint + (titleContourExtents.MaxPoint - titleContourExtents.MinPoint).OrthoProjectTo(new Vector3d(0, -1, 0)),
  69.                 titleContourExtents.MaxPoint + (selectionExtents.MaxPoint - titleContourExtents.MaxPoint).OrthoProjectTo(new Vector3d(0, -1, 0))
  70.             );
  71.         }
  72.  
  73.         private void AddAllProfile(Editor ed, ref AlxdProfileSource profileSource)
  74.         {
  75.             PromptSelectionOptions pso = new PromptSelectionOptions();
  76.             pso.MessageForAdding = "Выберите один профиль целиком";
  77.  
  78.             PromptSelectionResult psres;
  79.             psres = ed.GetSelection(pso);
  80.  
  81.             if (psres.Status != PromptStatus.OK)
  82.             {
  83.                 ed.WriteMessage("\nНичего не выбрано.");
  84.                 return;
  85.             }
  86.             else
  87.             {
  88.                 ObjectId[] objIds = psres.Value.GetObjectIds();
  89.                 if (objIds.Count() == 0)
  90.                 {
  91.                     ed.WriteMessage("\nНичего не выбрано.");
  92.                     return;
  93.                 }
  94.  
  95.                 using (Transaction tr = ed.Document.TransactionManager.StartTransaction())
  96.                 {
  97.                     Extents3d selectionExtents = GetExtents(tr, objIds);
  98.  
  99.                     profileSource.groundElevationsTitleObjectId = FindTitle(tr, objIds, this.groundElevationTitlePatterns);
  100.                     profileSource.distanceBetweenElevationsTitleObjectId = FindTitle(tr, objIds, this.distanceBetweenElevationsTitlePatterns);
  101.  
  102.                     Extents3d groundElevationsDataExtents;
  103.                     try
  104.                     {
  105.                         FindDataContour(ed, tr, profileSource.groundElevationsTitleObjectId, selectionExtents, out groundElevationsDataExtents);
  106.  
  107.                         TypedValue[] tvs = new TypedValue[] {
  108.                                     new TypedValue((int)DxfCode.Start, "TEXT,MTEXT")
  109.                                 };
  110.                         SelectionFilter filter = new SelectionFilter(tvs);
  111.  
  112.                         psres = ed.SelectCrossingWindow(groundElevationsDataExtents.MinPoint, groundElevationsDataExtents.MaxPoint);
  113.  
  114.                         if (psres.Status == PromptStatus.OK)
  115.                         {
  116.                             AlxdGroundElevations src = profileSource.groundElevations;
  117.  
  118.                             ObjectId[] tt = psres.Value.GetObjectIds().Where(item => !src.validGroundElevation.Keys.Contains(item) && !src.failedGroundElevation.Keys.Contains(item)).ToArray();
  119.  
  120.                             if (tt.Count() > 0)
  121.                             {
  122.                                 AlxdGroundElevations tmp = ValidateGroundElevation(new ObjectIdCollection(tt));
  123.                                 if (tmp != null)
  124.                                 {
  125.                                     profileSource.groundElevations.validGroundElevation = profileSource.groundElevations.validGroundElevation.Concat(tmp.validGroundElevation).ToDictionary(x => x.Key, x => x.Value);
  126.                                     profileSource.groundElevations.failedGroundElevation = profileSource.groundElevations.failedGroundElevation.Concat(tmp.failedGroundElevation).ToDictionary(x => x.Key, x => x.Value);
  127.                                 }
  128.                             }
  129.                         }
  130.                         else
  131.                         {
  132.                             ed.WriteMessage("Не удалось выбрать отметки земли в подвале профиля!");
  133.                         }
  134.                     }
  135.                     catch (System.Exception ex)
  136.                     {
  137.                         ed.WriteMessage(ex.Message);
  138.                     }
  139.  
  140.                     Extents3d distanceBetweenElevationsDataExtents;
  141.                     try
  142.                     {
  143.                         FindDataContour(ed, tr, profileSource.distanceBetweenElevationsTitleObjectId, selectionExtents, out distanceBetweenElevationsDataExtents);
  144.  
  145.                         TypedValue[] tvs = new TypedValue[] {
  146.                                     new TypedValue((int)DxfCode.Start, "TEXT,MTEXT,LINE,LWPOLYLINE")
  147.                                 };
  148.                         SelectionFilter filter = new SelectionFilter(tvs);
  149.  
  150.                         psres = ed.SelectCrossingWindow(distanceBetweenElevationsDataExtents.MinPoint, distanceBetweenElevationsDataExtents.MaxPoint);
  151.  
  152.                         if (psres.Status == PromptStatus.OK)
  153.                         {
  154.                             AlxdDistanceBetweenElevations src = profileSource.distanceBetweenElevations;
  155.  
  156.                             ObjectId[] tt = psres.Value.GetObjectIds().Where(item =>
  157.                                 !src.validTextDistanceBetweenElevations.Keys.Contains(item) &&
  158.                                 !src.failedTextDistanceBetweenElevations.Keys.Contains(item) &&
  159.                                 !src.validLineDistanceBetweenElevations.Keys.Contains(item) &&
  160.                                 !src.failedLineDistanceBetweenElevations.Keys.Contains(item)).ToArray();
  161.  
  162.                             if (tt.Count() > 0)
  163.                             {
  164.                                 AlxdDistanceBetweenElevations tmp = ValidateDistanceBetweenElevation(new ObjectIdCollection(tt));
  165.                                 if (tmp != null)
  166.                                 {
  167.                                     profileSource.distanceBetweenElevations.validTextDistanceBetweenElevations = profileSource.distanceBetweenElevations.validTextDistanceBetweenElevations.Concat(tmp.validTextDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
  168.                                     profileSource.distanceBetweenElevations.failedTextDistanceBetweenElevations = profileSource.distanceBetweenElevations.failedTextDistanceBetweenElevations.Concat(tmp.failedTextDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
  169.                                     profileSource.distanceBetweenElevations.validLineDistanceBetweenElevations = profileSource.distanceBetweenElevations.validLineDistanceBetweenElevations.Concat(tmp.validLineDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
  170.                                     profileSource.distanceBetweenElevations.failedLineDistanceBetweenElevations = profileSource.distanceBetweenElevations.failedLineDistanceBetweenElevations.Concat(tmp.failedLineDistanceBetweenElevations).ToDictionary(x => x.Key, x => x.Value);
  171.                                 }
  172.                             }
  173.                         }
  174.                         else
  175.                         {
  176.                             ed.WriteMessage("Не удалось выбрать расстояния между отметками в подвале профиля!");
  177.                         }
  178.                     }
  179.                     catch (System.Exception ex)
  180.                     {
  181.                         ed.WriteMessage(ex.Message);
  182.                     }
  183.                    
  184.                     tr.Commit();
  185.                 }
  186.                
  187.             }
  188.         }
  189.  



selectionExtents - это границы всех объектов выбранных пользователем;
groundElevationsDataExtents - это вычисленные границы ячейки таблицы с данными;
ed.SelectCrossingWindow - выбор объектов с фильтром по типам.

Немного смущает использование конструкции OrthoProjectTo(new Vector3d(0, -1, 0)), но пока лучше не придумал.

Пока все работает на тестовых профилях. Сегодня обещали принести профили от других изыскателей, будет на чем протестировать.

Оффлайн Алексей (IdeaSoft)

  • ADN
  • *
  • Сообщений: 1189
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
А вот эти тестовые линии-указатели линии для чего?
Они показывают связь между отметками и длинами?
А конечная цель задачи какая. Для чего нужны расстояния и отметки.
Для каких расчетов. Может задачу можно по другому решить.

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

  • ADN Club
  • **
  • Сообщений: 78
  • Карма: 2
Я бы назвал их не "тестовыми", а "вспомогательными". Серые и рыженькие линии - это линии соответствия отметок земли и расстояний между ними. Частенько в длинных профилях их количество не соответствует ожидаемому и выискивать ошибку, просматривая весь профиль глазами, очень муторно. Автоматически исключить какие-то "лишние" отметки или расстояния нельзя, только с участием человека. Поэкспериментировав пришел к выводу, что вспомогательные линии здорово упростят поиск потенциально неверных данных. Как только вспомогательные линии от значения расстояния к двум отметкам земли оказываются в одном квадранте, так сразу раскрашиваются в рыжий цвет, как бы намекая, что где-то тут может быть ошибка. Может и не быть ошибки, просто так текстовые объекты расположены. Поэтому надо, чтобы поглядел человек, проанализировал и понял, есть ошибка или нет.
Конечная цель - получить текстовый файл со значениями отметок, расстояний и еще данных с плана этого же профиля. Этот файл будет иметь расширение .geo, который можно использовать как источник исходных данных для GeoSeries.

Оффлайн Алексей (IdeaSoft)

  • ADN
  • *
  • Сообщений: 1189
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Т.е. в итоге для ГеоСериес нужна модель поверхности по данной трассе что ли?
Если нужна модель поверхности то почему привязка к текстам а не к отрезкам или полилиниям,
которыми нарисована поверхность?

И потом почему такая путаница получается на картинке
расстоянию в 13 м. должны соотв. отметки 66.13 и 65.83
Я так понял это линия поверхности земли длиной в 13 м с отметками по обеим сторонам.
Если так, то алгоритм просто должен искать для текста "13"
ближайшую отметку справа и ближайшую отметку слева и проблем с путаницей не будет.

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

  • ADN Club
  • **
  • Сообщений: 78
  • Карма: 2
То, что нужно GeoSeries поверхностью не назвать. Это профиль трассы: вид сбоку и вид сверху. Поверхность, в моем понимании, трехмерный объект :)
То, что на картинке, не путаница, а нарочно созданная ошибка для проверки работы алгоритма. Одна отметка 66.13 лишняя. Пользователь, после автоматического выбора всех исходных данных, должен вручную убрать лишнюю отметку и все вспомогательные линии выровняются.
В конечном счете должно быть вот так:


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

  • ADN Club
  • **
  • Сообщений: 78
  • Карма: 2
Приплыл...
Оказывается функция TraceBoundary очень медлительная функция, если в чертеже тысячи объектов. Блиииин...

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

  • Administrator
  • *****
  • Сообщений: 13877
  • Карма: 1785
  • Рыцарь ObjectARX
  • Skype: rivilis
Оказывается функция TraceBoundary очень медлительная функция, если в чертеже тысячи объектов. Блиииин...
Её скорость точно такая, как у команды _BOUNDARY.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • **
  • Сообщений: 78
  • Карма: 2
Я понял. Хотел применять ее для больших профилей, а получается, что приемлемо она только для маленьких работает, где она и не нужна вовсе. Можно и так все выбрать. :(

Оффлайн Алексей (IdeaSoft)

  • ADN
  • *
  • Сообщений: 1189
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Я понял. Хотел применять ее для больших профилей, а получается, что приемлемо она только для маленьких работает, где она и не нужна вовсе. Можно и так все выбрать. :(
Тут дело скорее не в размере профиля, а в том что при выборке объектов через SelectionSet
попадающих в прямоугольную область в набор попадут только те объекты, которые попали в видимость экрана монитора.
Те объекты, которые не попали в видимую часть монитора в набор не попадут. Я с этой проблемой еще лет 7 назад столкнулся,
когда делал выборку объектов в плане сетей по прямоугольным областям.
Так что ели хочешь полный набор объектов нужно делать Zoom ALL по всему пространству модели.

Оффлайн Алексей (IdeaSoft)

  • ADN
  • *
  • Сообщений: 1189
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Я понял. Хотел применять ее для больших профилей, а получается, что приемлемо она только для маленьких работает, где она и не нужна вовсе. Можно и так все выбрать. :(
Тут дело скорее не в размере профиля, а в том что при выборке объектов через SelectionSet
попадающих в прямоугольную область в набор попадут только те объекты, которые попали в видимость экрана монитора.
Те объекты, которые не попали в видимую часть монитора в набор не попадут. Я с этой проблемой еще лет 7 назад столкнулся,
когда делал выборку объектов в плане сетей по прямоугольным областям.
Так что ели хочешь полный набор объектов нужно делать Zoom ALL по всему пространству модели.