Инструменты пользователя

Инструменты сайта


Боковая панель

developers:tutorial:modelviewexport

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

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

  1. «generate_planchet» - для формирования чертежа плана
  2. «generate_visualization_map» - для формирования сводной модели

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

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

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

  • Topomatic.Cad.Foundation.dll - базовые математические типы и операции
  • Topomatic.Cad.View.dll - видовой экран
  • Topomatic.Dtm - цифровая модель местности
  • Topomatic.Dwg - чертёж
  • Topomatic.Planchet.Runtime - формирование планшета
  • Topomatic.Visualization - сводная модель
  • Topomatic.Visualization.Runtime - формирование сводной модели

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

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

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

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

  1. Создать команду для добавления примитивов на чертёж
  2. Зарегистрировать вызов команды через бродкаст «generate_planchet» в plugin-файле

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

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

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

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

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

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

Model.cs
//функция срабатывающая при обявлении бродкаста "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»

tut_export_model_views.plugin
"broadcasts": {
  "generate_planchet": "tutorial_generate_planchet"
}

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

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

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

  1. Создать команду для добавления объектов в сводную ИМ
  2. Зарегистрировать вызов команды через бродкаст «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».

Module.cs
//функция срабатывающая при объявлении бродкаста "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»

tut_export_model_views.plugin
"broadcasts": {
  "generate_visualization_map": "tutorial_generate_visualization"
}

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

Исходный код примера расположен в проекте «TutorialExportModelViews».
developers/tutorial/modelviewexport.txt · Последние изменения: 2023/01/10 11:12 — proxor