Экспорт чертежа и объектов информационной модели

Программный комплекс Топоматик Робур предоставляет пользователю возможность управления формированием чертежей плана и сводной модели. Для этого необходимо реализовать поддержку бродкастов:

«generate_planchet» - для формирования чертежа плана

«generate_visualization_map» - для формирования сводной модели

В этом примере мы сформируем примитивы на основе данных нашей модели и поместим их в экспортируемый плоский чертёж. Также мы сформируем объекты на основе данных нашей модели и поместим их в экспортируемую сводную ИМ.

Подготовка модуля

Создайте и настройте новый модуль для подключения к программному комплексу Топоматик Робур. С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:

Topomatic.Cad.Foundation.dll - базовые математические типы и операции

Topomatic.Cad.View.dll - видовой экран

Topomatic.Dtm - цифровая модель местности

Topomatic.Dwg - чертёж

Topomatic.Planchet.Runtime - формирование планшета

Topomatic.Visualization - сводная модель

Topomatic.Visualization.Runtime - формирование сводной модели

Подготовка проекта

Добавим элементы нашей модели на план. В нашем случае это будет выглядеть так:

Экспорт элементов модели в плоский чертёж

Для экспорта элементов пользовательской модели в плоский чертёж необходимы следующий действия:

Создать команду для добавления примитивов на чертёж

Зарегистрировать вызов команды через бродкаст «generate_planchet» в plugin-файле

Создание команды добавления примитивов на чертёж

Команда должна вызвать метод принимающий в качестве аргумента объект типа GeneratePlanchetEventArgs.

Свойство GeneratePlanchetEventArgs.Layer возвращает текущий экспортируемый слой типа CadViewLayer. С помощью этого свойства убедимся, что слой является слоем нашей модели.

Свойство GeneratePlanchetEventArgs.Model возвращает корневую область модели чертежа типа DwgBlock, в которую следует добавить необходимые примитивы (подклассы DwgEntity).

Модель чертежа можно получить с помощью свойства DwgBlock.Drawing. Прежде чем вносить изменения в чертёж мы зафиксируем его настройки методом Drawing.PushState() и выполним их сброс до состояния по умолчанию методом Drawing.ResetStateDefault(). Далее в блоке try внесём необходимые изменения в чертёж и в блоке finally вернём настройки чертежа в прежнее состояние методом Drawing.PopState().

В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd».

//функция срабатывающая при обявлении бродкаста "generate_planchet" //подписка на бродкаст осудествляется в plugin-файле в разделе "broadcasts" [cmd("tutorial_generate_planchet")] private void GeneratePlanchet(GeneratePlanchetEventArgs e) { // проверяем принадлежит ли слой нашей модели if (e.Layer is ModelLayer) { // отрисовываем примитивы GenerateModelPlanchet(e); e.Handled = true; } } /// <summary> /// Формирование примитивов на основе данных полученных из модели /// и добавление примитивов в чертёж /// </summary> /// <param name="e"></param> private void GenerateModelPlanchet(GeneratePlanchetEventArgs e) { if (e.Layer is ModelLayer layer) { // определяем префикс имени слоя var path = e.Path; if (e.SaveFullPath) { path += "."; } else { path = string.Empty; } // если слой включен и относится к нашему типу модели // делаем активным необходимый слой чертежа и добавляем примитивы в чертёж if (layer.Visible && layer.Model is Model model) { var modelSpace = e.Model; var drawing = modelSpace.Drawing; // запоминаем настройки чертежа и сбрасываем к значениям по умолчанию drawing.PushState(); try { drawing.ResetStateDefault(); var layerName = path + layer.Name; var dwgLayer = drawing.Layers[layerName] ?? drawing.Layers.Add(layerName); drawing.ActiveLayer = dwgLayer; for (int i = 0; i < model.Count; i++) { var points = model[i]; var vertices = new List<Vector2D>(); for (int j = 0; j < points.Count; j++) { var point = points[j]; var text = modelSpace.AddText(j.ToString(), point, 2, 1, 0, 0); text.Color = new CadColor(Color.Orange); vertices.Add(point); } var pline = modelSpace.AddPolyline(vertices); pline.Color = CadColor.Yellow; } } finally { // восстанавливаем предыдущие настройки чертежа drawing.PopState(); } } } }

Команда «tutorial_generate_planchet» должна выполняться в момент создания планшета или экспорта ситуации. Для этого добавим соответствующую запись в plugin-файл в секции «broadcast»

"broadcasts": { "generate_planchet": "tutorial_generate_planchet" }

В результате работы команды «tutorial_generate_planchet» в экспортированном файле плоского чертежа появятся примитивы нашей модели.

Экспорт объектов в сводную ИМ

Для экспорта объектов пользовательской модели в сводную ИМ необходимы следующий действия:

Создать команду для добавления объектов в сводную ИМ

Зарегистрировать вызов команды через бродкаст «generate_visualization_map» в plugin-файле

Создание команды добавления объектов в сводную ИМ

Команда должна вызвать метод принимающий в качестве аргумента объект типа CreateVisualizationEventArgs.

Свойство CreateVisualizationEventArgs.Layer возвращает текущий экспортируемый слой типа CadViewLayer. По свойству Layer можно определить, какая именно модель в данный момент экспортируется.

Свойство CreateVisualizationEventArgs.Map возвращает сводную ИМ типа VisualizationMap, в которую следует добавить необходимые объекты.

Объекты ИМ объединяются в группы (VisualizationGroup). Каждая группа - это узел в дереве сводной модели. Текущую группу можно получить через свойство VisualizationMap.Group, и при необходимости переопределить её свойства через метод VisualizationGroup.ApplayOverridedProperties() или тип. Группы так же могут содержать в себе другие группы. Новая группа создаётся методом VisualizationMap.BeginGroup() и становится текущей. Метод VisualizationGroup.EndGroup() заканчивает работу с текущей группой и возвращает группу уровнем выше.

Связку BeginGroup() => EndGroup() следует использовать в сочетании с блоком try/finally. # args - аргументы типа CreateVisualizationEventArgs VisualizationMap map = args.Map; map.BeginGroup("GroupName", false); try { VisualizationGroup group = map.Group; # пользовательские действия } finally { map.BeginGroup("GroupName", false); }

Для добавления моделей в сводную ИМ используется метод VisualizationMap.AddMesh(), а для размещения добавленной модели в пространстве метод VisualizationMap.AddInsertion().

Добавление объекта в сводную модель

Наша модель содержит в себе линейные объекты. В качестве примера для отображения на сводной модели примем, что линейный объект нашей модели это забор (Fence) с фиксированной высотой 3 метра.

Для каждого участка забора создадим полигональную сеть (MeshGeometry3D).

Через свойство MeshGeometry3D.Groups получим коллекцию групп сети (MaterialGroupsCollection) и добавим в неё группу с материалом (MaterialGroup).

Определим координаты вершин (Vector3F) и добавим их в коллекцию вершин (Vector3dCollection) через свойство MeshGeometry3D.Positions.

Определим грани (Face) опирающиеся на индексы вершин полигональной сети. Для каждой грани добавим в группу сети новый индекс методом MaterialGroup.AddIndex(). Через свойство MeshGeometry3D.TriangleIndices получим коллекцию треугольников (TrianglesCollection) и добавим в неё грани.

Создадим трёхмерную модель (GeometryModel3D). Получим словарь с материалами через свойство GeometryModel3D.Materials и добавим в него материал (PhongMaterial). Через свойство GeometryModel3D.Meshes получим словарь полигональных сетей и добавим в него вышеописанную сеть.

Сгенерируем новое имя для нашей 3D-модели методом VisualizationMap.GenMeshName(). Добавим нашу 3D-модель с новым именем в коллекцию моделей сводной ИМ методом VisualizationMap.AddMesh.

Создадим новую группу методом VisualizationMap.BeginGroup(). В блоке try добавим вставку (GeometryInsertion) ранее добавленной 3D-модели методом VisualizationMap.AddInsertion. Через свойство VisualizationMap.Group получим текущую группу (VisualizationGroup). Назначим ей тип через свойство VisualizationGroup.Type. Через свойство VisualizationGroup.Properties получим коллекцию свойств (ImProperties) и добавим в неё новые свойства (ImProperty) «Количество узлов» и «Длина».

В блоке finally закроем группу методом VisualizationMap.EndGroup().

В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd».

//функция срабатывающая при объявлении бродкаста "generate_visualization_map" //подписка на бродкаст осуществляется в plugin-файле в разделе "broadcasts" [cmd("tutorial_generate_visualization")] private void GenerateVisualization(CreateVisualizationEventArgs args) { // проверяем слой на соответствие типу слоя нашей модели if (args.Layer is ModelLayer) { GenerateModelVisualization(args); args.Handled = true; } } /// <summary> /// Формирование элементов информационной модели на основе данных полученных из модели /// и добавление их в информационную модель /// </summary> /// <param name="args"></param> private void GenerateModelVisualization(CreateVisualizationEventArgs args) { var fenceHeight = 3.0; // высота забора var map = args.Map; // сводная модель var layer = args.Layer as ModelLayer; // проверяем слой на видимость и наличием нашей модели if (layer.Visible && layer.Model is Model model) { // собираем поверхности для определения высотного положения забора var project = ApplicationHost.Current.ActiveProject as ModelProject; var items = project.Model.GetChilds(); var surfaces = new List<ISurface>(); foreach (var item in items) { if (item.ModelType.Equals("dtm", StringComparison.OrdinalIgnoreCase) && item.Model is TerrainModel dtm) { var sfc = args.FetchSurfaces(dtm); surfaces.Add(sfc); } } map.BeginGroup("CustomModel", false); try { for (int i = 0; i < model.Count; i++) { // создаём полигональную сеть и добавляем в неё группу материалов var mesh = new MeshGeometry3D(); var materialGroup = new MaterialGroup { Material = "Fence_Material" }; mesh.Groups.Add(materialGroup); // наполняем сеть вершинами и определяем грани опирающиеся на эти точки var length = 0.0; //длина участка забора var points = model[i]; for (int j = 0; j < points.Count; j++) { var point = points[j]; var elevation = 0.0; foreach (var sfc in surfaces) { var z = sfc.GetElevation(point); if (z.HasValue && ValueConverter.CompValues(z.Value, elevation) == 1) { elevation = z.Value; } } mesh.Positions.Add(new Vector3F(new Vector3D(point, elevation))); mesh.Positions.Add(new Vector3F(new Vector3D(point, elevation + fenceHeight))); if (j < points.Count - 1) { var nextPoint = points[j + 1]; var delta = nextPoint - point; length += delta.Length; var face1 = new Face(j * 2, (j + 1) * 2, j * 2 + 1); var face2 = new Face((j + 1) * 2, (j + 1) * 2 + 1, j * 2 + 1); materialGroup.AddIndex(TrimShort(mesh.TriangleIndices.Count)); mesh.TriangleIndices.Add(face1); materialGroup.AddIndex(TrimShort(mesh.TriangleIndices.Count)); mesh.TriangleIndices.Add(face2); } } // создаём 3D-модель и добавляем в неё материал // добавляем сеть в 3D-модель var geometry = new GeometryModel3D(); if (!geometry.Materials.ContainsKey("Custom_Material")) { var material = new PhongMaterial(); material.Diffuse = new Vector3F(0.6, 0.6, 0.6); material.Shininess = 0.8f; material.SpecularLevel = 0.8f; geometry.Materials["Custom_Material"] = material; } geometry.Meshes["Fence_Mesh"] = mesh; // добавляем 3D-модель в сводную модель, // назначаем ей тип и необходимые свойства var meshName = map.GenMeshName("custom_mesh"); map.AddMesh(meshName, geometry); map.BeginGroup("Fence", false); try { map.AddInsertion(meshName, new Vector3D(0, 0, 0)); var type = map.FindType("SmdxCustomFence"); if (type == null) { var parent = map.FindType("SmdxElement"); var typeDesc = new ImTypeDescriptor("SmdxCustomFence", "Пользовательский забор", parent); type = map.CreateType(typeDesc); } map.Group.Type = type; map.Group.Properties.Add(new ImProperty("points", "Количество узлов", points.Count)); map.Group.Properties.Add(new ImProperty("length", "Длина", length)); } finally { map.EndGroup(); } } } finally { map.EndGroup(); } } } private static ushort TrimShort(int value) { Debug.Assert(value <= ushort.MaxValue); return value > ushort.MaxValue ? ushort.MaxValue : (ushort)value; }

Команда «tutorial_generate_visualization» должна выполняться в момент создания сводной модели. Для этого добавим соответствующую запись в plugin-файл в секции «broadcast»

"broadcasts": { "generate_visualization_map": "tutorial_generate_visualization" }

В результате работы команды «tutorial_generate_visualization» в экспортированной сводной модели появятся объекты нашей модели.

Исходный код примера расположен в проекте «TutorialExportModelViews».