Здесь показаны различия между двумя версиями данной страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
developers:tutorial:sfcselection [2022/03/02 20:59] proxor [Выделение структурных линий] |
developers:tutorial:sfcselection [2022/04/09 18:55] (текущий) proxor [Выделение горизонталей] |
||
---|---|---|---|
Строка 1: | Строка 1: | ||
====== Выделение элементов поверхности ====== | ====== Выделение элементов поверхности ====== | ||
- | Нередко, при работе с поверхностью, пользователю требуется изменять или получать информацию из её элементов. В этой главе мы рассмотрим простые примеры по работе с элементами поверхности. | + | Нередко, при работе с [[road:work_with_cmm:create_and_edit_surface_new:basic_terms_and_definition_new|поверхностью]], пользователю требуется изменять или получать информацию из её элементов. В этой главе мы рассмотрим простые примеры по работе с элементами поверхности. |
- | В качестве основного инструмента получения элементов поверхности рекомендуется пользоваться методами класса ''SurfaceLayer''. Некоторые из них мы рассмотрим в следующих примерах. Сперва выберем необходимые нам объекты и начнём мы с выбора точек поверхности. | + | Программно, поверхность представляет из себя экземпляр класса [[developers:references:topomatic.sfc.surface|Surface]]. Этот класс содержит в себе все элементы поверхности. |
+ | Основные элементы класса [[developers:references:topomatic.sfc.surface|Surface]] это: | ||
+ | * [[developers:references:topomatic.sfc.surface.points|Surface.Points]] - массив [[road:work_with_cmm:create_and_edit_surface_new:notion_dots_new|точек поверхности]] типа [[developers:references:topomatic.sfc.surfacepointarray|SurfacePointArray]] | ||
+ | * [[developers:references:topomatic.sfc.surface.structurelines|Surface.StructureLines]] - коллекция [[road:work_with_cmm:create_and_edit_surface_new:notion_structural_lines_new|структурных линий]] содержащихся в поверхности типа [[developers:references:topomatic.sfc.structurelines|StructureLines]] | ||
+ | * [[developers:references:topomatic.sfc.surface.triangles|Surface.Triangles]] - массив треугольников отписывающих кривизну рельефа типа [[developers:references:topomatic.sfc.surfacetrianglearray|SurfaceTriangleArray]] | ||
+ | * [[developers:references:topomatic.sfc.surface.patchs|Surface.Patchs]] - массив [[road:work_with_cmm:create_and_edit_surface_new:notion_of_parcels_new|участков]] типа [[developers:references:topomatic.sfc.surfacepatcharray|SurfacePatchArray]] | ||
+ | |||
+ | Базовыми элементами поверхности являются точки поверхности и структурные линии. Опираясь на них, в модели поверхности формируется триангуляция состоящая из треугольников, которые в свою очередь способны объединяться в участки, а так же на основе которых генерируются горизонтали. Более подробно о работе с элементами поверхности в программном комплексе [[http://topomatic.ru|Топоматик Робур]] можно ознакомиться [[road:work_with_cmm:create_and_edit_surface_new:start|здесь]]. | ||
+ | |||
+ | Прежде чем приступить работе с элементами поверхности, создайте и настройте новый [[developers:tutorial:module|модуль]] для подключения к программному комплексу [[http://www.topomatic.ru|Топоматик Робур]]. | ||
+ | |||
+ | С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки: | ||
+ | * [[developers:references:topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции | ||
+ | * [[developers:references:topomatic.cad.view|Topomatic.Cad.View.dll]] - элемент управления для отображения слоёв моделей | ||
+ | * [[developers:references:topomatic.dwg|Topomatic.Dwg.dll]] - примитивы и модель чертежа | ||
+ | * [[developers:references:topomatic.sfc|Topomatic.Sfc.dll]] - поверхность | ||
+ | * [[developers:references:topomatic.sfc.layer|Topomatic.Sfc.Layer.dll]] - слой поверхности | ||
+ | |||
+ | В качестве основного инструмента получения элементов поверхности рекомендуется пользоваться методами слоя поверхности ''SurfaceLayer''. Для получения текущего ''SurfaceLayer'', необходимо воспользоваться статическим методом ''SurfaceLayer.GetSurfaceLayer(cadview)''. В качестве аргумента метод принимает видовой экран [[developers:references:topomatic.cad.view.cadview|CadView]], который мы получаем пользуясь свойством ''CadView'' [[developers:tutorial:module|нашего модуля]]. | ||
+ | |||
+ | ==== Множественный выбор ==== | ||
+ | ''SurfaceLayer'' предоставляет широкий набор инструментов для выделения элементов поверхности. Ниже описаны методы позволяющие пользователю осуществить выбор: | ||
+ | * SurfaceLayer.SelectPoints() - выделение [[road:work_with_cmm:create_and_edit_surface_new:notion_dots_new|точек поверхности]] | ||
+ | * SurfaceLayer.SelectStructureLines() - выделение [[road:work_with_cmm:create_and_edit_surface_new:notion_structural_lines_new|структурных линий]] | ||
+ | * SurfaceLayer.SelectPatches() - выделение [[road:work_with_cmm:create_and_edit_surface_new:notion_of_parcels_new|участков]] | ||
+ | |||
+ | Все перечисленные методы принимают одинаковый набор аргументов и возвращают результат выполнения пользовательского запроса типа [[developers:references:topomatic.cad.view.getpointresult|GetPointResult]]. | ||
+ | |||
+ | В качестве первого аргумента, принимается предикат выполняющий роль фильтра выделяемых объектов. Предикат должен возвращать значение типа ''bool'', которое указывает на необходимость выделения объекта описанного в предикате. На входе у предиката принимается индекс выбираемого элемента в множестве соответствующем его типу. Например мы хотим ограничиться выбором только ситуационных точек, тогда код должен выглядеть следующим образом: | ||
+ | |||
+ | <code csharp> | ||
+ | var result = surfaceLayer.SelectPoints(i => surfaceLayer.Surface.Points[i].IsSituation, "Выделите ситуационные точки:"); | ||
+ | </code> | ||
+ | |||
+ | В качестве второго аргумента принимается текст сообщения выводящийся пользователю на экран во время выполнения команды. | ||
+ | |||
+ | Третий представляет из себя массив строк. Аргумент является опциональным и может не использоваться. Он позволяет предоставить пользователю возможность осуществить выбор из предложенных ему вариантов. | ||
+ | |||
+ | Возвращаемое значение [[developers:references:topomatic.cad.view.getpointresult|GetPointResult]] указывает на то, какое действие было совершено пользователем. Пользователь может выделить элементы поверхности и подтвердить выбор (''Accept''), отменить действие (''Cancel'') или выбрать один из предложенных вариантов контекстного меню (''UserCmd''). | ||
+ | |||
+ | <note>Значение ''UserCmd'' возвращается только в том случае, если в качестве третьего аргумента был передан массив строк и пользователь выбрал одно из предложенных значений. Чтобы понять какой именно пункт был выбран пользователем, следует воспользоваться свойством [[developers:references:topomatic.cad.view.cadview.lastusercmd|CadView.LastUserCmd]].</note> | ||
+ | |||
+ | ==== Одиночный выбор ==== | ||
+ | Так же ''SurfaceLayer'' располагает возможностью выделения одного конкретного объекта: | ||
+ | * SurfaceLayer.PickOnePoint() - выделение одной [[road:work_with_cmm:create_and_edit_surface_new:notion_dots_new|точки поверхности]]. В качестве аргументов принимаются предикат-фильтр, аналогично описанному ранее, переменная типа ''int'' с модификатором ''out'', в которую будет сохранён индекс выбранной точки, сообщение пользователю и массив строк с вариантами пользовательского выбора. Возвращаемое значение [[developers:references:topomatic.cad.view.getpointresult|GetPointResult]], описанное выше. | ||
+ | * SurfaceLayer.SelectOneStructureLine() - выделение одной [[road:work_with_cmm:create_and_edit_surface_new:notion_structural_lines_new|структурной линии]]. Аргументы предикат-фильтр и сообщение пользователю. Метод возвращает саму [[road:work_with_cmm:create_and_edit_surface_new:notion_structural_lines_new|структурную линию]]. | ||
+ | * SurfaceLayer.SelectOnePatch() - выбор одного [[road:work_with_cmm:create_and_edit_surface_new:notion_of_parcels_new|участка]]. Аргументы предикат-фильтр и сообщение пользователю. Метод возвращает индекс участка из [[road:work_with_cmm:create_and_edit_surface_new:notion_of_parcels_new|массива участков]]. | ||
===== Выделение точек поверхности ===== | ===== Выделение точек поверхности ===== | ||
В этом примере мы выберем точки поверхности на вкладке "План" и рассчитаем их среднюю отметку. | В этом примере мы выберем точки поверхности на вкладке "План" и рассчитаем их среднюю отметку. | ||
Для этого воспользуемся методом ''SelectPoints()'' у слоя текущей поверхности (''SurfaceLayer''). Так как метод ''SelectPoints()'' предоставляет пользователю возможность выбора элементов контекстного меню, то возвращаемое значение будет иметь вид ''GetPointResult'', которое укажет на то, какое именно действие было совершено пользователем. | Для этого воспользуемся методом ''SelectPoints()'' у слоя текущей поверхности (''SurfaceLayer''). Так как метод ''SelectPoints()'' предоставляет пользователю возможность выбора элементов контекстного меню, то возвращаемое значение будет иметь вид ''GetPointResult'', которое укажет на то, какое именно действие было совершено пользователем. | ||
- | Для того чтобы получить множество выбранных точек, будет необходимо воспользоваться методом ''GetSelectedPoints()'' у того же ''SurfaceLayer''. Далее пройдёмся по выбранным точкам циклом ''foreach'' и применим необходимую логику для расчёта средней отметки. | + | Для того чтобы получить множество выбранных точек, будет необходимо воспользоваться методом ''GetSelectedPoints()'' у того же ''SurfaceLayer''. |
- | Ниже представлен код примера, который мы оформим в качестве метода ''CalculateAverageElevation()'' с атрибутом ''[cmd("calculate_average_elevation")]''. Данный метод мы поместим в класс ''Module'' для возможности последующего вызова этой команды из командной строки. | + | В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда "calculate_average_elevation" предложит пользователю выбрать точки поверхности и посчитает их среднюю отметку. |
<code csharp> | <code csharp> | ||
Строка 48: | Строка 94: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | {{ :developers:tutorial:surfaceelementsselect:tutselpoints.png?nolink&600 |}} | ||
+ | |||
+ | |||
+ | |||
===== Выделение структурных линий ===== | ===== Выделение структурных линий ===== | ||
- | В данном примере мы попробуем рассчитать максимальный уклон среди всех сегментов структурной линии. | + | В данном примере мы рассчитаем максимальный уклон среди всех сегментов структурной линии. |
- | Как и в предыдущем примере, для выбора структурной линии нам потребуется слой текущей поверхности (''SurfaceLayer''). Чтобы выбрать одну структурную линию, воспользуемся методом ''SelectOneStructureLine()''. Здесь возвращаемым значением сразу же будет выступать выбранная структурная линия (''StructureLine''), поэтому никаких дополнительных действий выполнять не придется. | + | Как и в предыдущем примере, для выбора структурной линии нам потребуется слой текущей поверхности (''SurfaceLayer''). Чтобы выбрать одну структурную линию, воспользуемся методом ''SelectOneStructureLine()''. Здесь возвращаемым значением сразу же будет выбранная [[developers:references:topomatic.sfc.structureline|структурная линия]], поэтому никаких дополнительных действий выполнять не придется. |
+ | |||
+ | <note>Важно помнить, что [[developers:references:topomatic.sfc.structurelinenode|узел структурной линии]] всегда опирается на [[developers:references:topomatic.sfc.surfacepoint|точку поверхности]], индекс которой хранится в поле [[developers:references:topomatic.sfc.structurelinenode.index|StructureLineNode.Index]]. Узел не несёт информации о его плановом положении, но при помощи индекса опорной точки, мы можем найти её в [[developers:references:topomatic.sfc.surfacepointarray|массиве точек поверхности]] и получить необходимые данные о расположении этого узла в пространстве.</note> | ||
+ | |||
+ | В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда «define_steepest_grade» предложит пользователю выбрать структурную линию и определит максимальный уклон среди всех её сегментов. | ||
+ | |||
+ | <code csharp> | ||
+ | { | ||
+ | ... | ||
+ | [cmd("define_steepest_grade")] | ||
+ | private void DefineSteepestGrade() | ||
+ | { | ||
+ | //Находим активный SurfaceLayer | ||
+ | var cadview = CadView; | ||
+ | var actSfcLayer = SurfaceLayer.GetSurfaceLayer(cadview); | ||
+ | if (actSfcLayer == null) return; | ||
+ | |||
+ | //Выбираем структурную линию | ||
+ | var strLine = actSfcLayer.SelectOneStructureLine(null, "Выберите структурную линию:"); | ||
+ | if (strLine == null) return; | ||
+ | |||
+ | //Обращаемся к узлам структурной линии, находим соответствующие им точки поверхности | ||
+ | //и высчитываем уклоны сегментов по пути определяя максимальный уклон | ||
+ | var sfc = actSfcLayer.Surface; | ||
+ | var maxIncline = 0.0; | ||
+ | for (var i = 0; i < strLine.Count - 1; i++) | ||
+ | { | ||
+ | var stPoint = sfc.Points[strLine[i].Index]; | ||
+ | var endPoint = sfc.Points[strLine[i + 1].Index]; | ||
+ | var delta = Math.Abs(stPoint.Vertex.Elevation - endPoint.Vertex.Elevation); | ||
+ | |||
+ | var stPos = stPoint.Vertex.Pos; | ||
+ | var endPos = endPoint.Vertex.Pos; | ||
+ | var length = (endPos - stPos).Length; | ||
+ | |||
+ | if (ValueConverter.CompValues(length, 0.0) != 0) | ||
+ | { | ||
+ | var incline = delta / length; | ||
+ | if (incline > maxIncline) maxIncline = incline; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | MessageDlg.Show($"Максимальный уклон: {maxIncline}"); | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | {{ :developers:tutorial:surfaceelementsselect:tutselstrline.png?nolink&600 |}} | ||
+ | ===== Выделение горизонталей ===== | ||
+ | По умолчанию, в программном комплексе [[http://topomatic.ru|Топоматик Робур]], выделение горизонталей отключено. Чтобы включить выделение горизонталей нужно изменить значение статического свойства ''SurfaceLayer.SurfaceSelectionSet.IsHorizontalSelectable'' на ''true''. Взаимодействовать с объектами горизонталей напрямую нельзя, но горизонтали реализуют интерфейс [[developers:references:topomatic.cad.foundation.ilinearobject|ILinearObject]], поэтому мы можем получить [[developers:references:topomatic.cad.foundation.polyline3d|трёхмерную полилинию]] описывающую положение горизонтали, воспользовавшись методом [[developers:references:topomatic.cad.foundation.ilinearobject.getpolyline_topomatic.cad.foundation.ipolyline3d|GetPolyline()]]. Для получения площади [[developers:references:topomatic.cad.foundation.polyline3d|трёхмерной полилинии]] воспользуемся методом расширения [[developers:references:topomatic.cad.foundation.polylineextentions.getarea2d_topomatic.cad.foundation.ipolyline3d|PolylineExtentions.GetArea2D(IPolyline3D)]] | ||
+ | |||
+ | В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда "get_horizontal_line_area" предложит пользователю выбрать замкнутую горизонталь и определит её площадь. | ||
+ | |||
+ | <code csharp> | ||
+ | ... | ||
+ | [cmd("get_horizontal_line_area")] | ||
+ | private void GetHorizontalLineArea() | ||
+ | { | ||
+ | //Находим активный SurfaceLayer | ||
+ | var cadview = CadView; | ||
+ | var actSfcLayer = SurfaceLayer.GetSurfaceLayer(cadview); | ||
+ | if (actSfcLayer == null) return; | ||
+ | |||
+ | //Включаем возможность выбор горизонталей | ||
+ | var initSelectState = SurfaceLayer.SurfaceSelectionSet.IsHorizontalSelectable; | ||
+ | SurfaceLayer.SurfaceSelectionSet.IsHorizontalSelectable = true; | ||
+ | |||
+ | //Получаем указатель на текущий набор объектов | ||
+ | var ss = actSfcLayer.SelectionSet; | ||
+ | |||
+ | //Получаем указатель на слой горизонталей | ||
+ | var layer = actSfcLayer.Surface.Style.HorizontalsStyle.GetLayer(); | ||
+ | |||
+ | //Выбираем горизонталь на плане | ||
+ | var linear_object = ss.PickOneObjectAtScreen(obj => | ||
+ | { | ||
+ | if (obj is SurfaceObjectWrapper layered_object) | ||
+ | { | ||
+ | return layered_object.LayerID == layer.ObjectID; | ||
+ | } | ||
+ | return false; | ||
+ | }, "Выберите замкнутую горизонталь") as ILinearObject; | ||
+ | |||
+ | //Восстанавливаем состояние флага возможности выбора горизонталей | ||
+ | SurfaceLayer.SurfaceSelectionSet.IsHorizontalSelectable = initSelectState; | ||
+ | if (linear_object == null) return; | ||
+ | |||
+ | //Извлекаем полилинию из горизонтали и получаем её площадь | ||
+ | var poly = new Polyline3D(); | ||
+ | linear_object.GetPolyline(poly); | ||
+ | |||
+ | //Проверяем замкнута ли горизонталь | ||
+ | var startVec = poly[0]; | ||
+ | var endVec = poly[poly.Count - 1]; | ||
+ | if (startVec.EqualEps(endVec)) | ||
+ | { | ||
+ | var area = poly.GetArea2D(); | ||
+ | MessageDlg.Show($"Площадь горизонтали: {area}"); | ||
+ | } | ||
+ | else MessageDlg.Show($"Горизонталь не замкнута."); | ||
+ | } | ||
+ | ... | ||
+ | </code> | ||
+ | |||
+ | {{ :developers:tutorial:surfaceelementsselect:tutselhorizontal.png?nolink&600 |}} | ||
+ | |||
+ | |||
+ | |||
+ | Теперь необходимо сформировать наш файл .plugin. Заполните его следующим образом. | ||
+ | |||
+ | <code javascript> | ||
+ | { | ||
+ | "assemblies": { | ||
+ | "TutorialSelectSurfaceElements": { | ||
+ | "assembly": "TutorialSelectSurfaceElements.dll, TutorialSelectSurfaceElements.ModulePluginHost" | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | "actions": { | ||
+ | "id_calculate_average_elevation": { | ||
+ | "cmd": "calculate_average_elevation", | ||
+ | "title": "Рассчитать среднюю отметку" | ||
+ | }, | ||
+ | "id_define_steepest_grade": { | ||
+ | "cmd": "define_steepest_grade", | ||
+ | "title": "Рассчитать наибольший уклон" | ||
+ | }, | ||
+ | "id_get_horizontal_line_area": { | ||
+ | "cmd": "get_horizontal_line_area", | ||
+ | "title": "Получить площадь горизонтали" | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | "menubars": { | ||
+ | "rbproj": { | ||
+ | "items": [ | ||
+ | { | ||
+ | "id": "tutorial_menu", | ||
+ | "title": "Tutorial", | ||
+ | "items": [ | ||
+ | "id_calculate_average_elevation", | ||
+ | "id_define_steepest_grade", | ||
+ | "id_get_horizontal_line_area" | ||
+ | ] | ||
+ | } | ||
+ | ] | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Результатом запуска проекта будет появление в главном меню пункта «Tutorial», с подпунктами, которые будут работать в соответствии с описанными выше алгоритмами. | ||
- | Воспользуемся циклом ''for'', чтобы обратиться к узлам структурной линии и в каждой итерации будет обращаться к текущей точке цикла и к следующей, тем самым получая представление о сегменте структурной линии. | + | <note>[[developers:tutorial:tutorialcode|Исходный код]] примера расположен в проекте **"TutorialSelectSurfaceElements"**.</note> |
- | <note>Для того чтобы мы не получили исключение в результате обращения к последующему узлу структурной линии в последней итерации, заведомо уменьшим количество итераций цикла на 1.</note> | + |