====== Экспорт чертежа и объектов информационной модели ======
Программный комплекс Топоматик Робур предоставляет пользователю возможность управления формированием чертежей плана и сводной модели. Для этого необходимо реализовать поддержку бродкастов:
- "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 - формирование сводной модели
==== Подготовка проекта ====
Добавим элементы нашей модели на план. В нашем случае это будет выглядеть так:
{{ :developers:tutorial:createmodel:tut_export_model_views_1.png?direct |}}
==== Экспорт элементов модели в плоский чертёж ====
Для экспорта элементов пользовательской модели в плоский чертёж необходимы следующий действия:
- Создать команду для добавления примитивов на чертёж
- Зарегистрировать вызов команды через бродкаст «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;
}
}
///
/// Формирование примитивов на основе данных полученных из модели
/// и добавление примитивов в чертёж
///
///
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();
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**" в экспортированном файле плоского чертежа появятся примитивы нашей модели.
{{ :developers:tutorial:createmodel:tut_export_model_views_3.png?direct |}}
==== Экспорт объектов в сводную ИМ ====
Для экспорта объектов пользовательской модели в сводную ИМ необходимы следующий действия:
- Создать команду для добавления объектов в сводную ИМ
- Зарегистрировать вызов команды через бродкаст «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;
}
}
///
/// Формирование элементов информационной модели на основе данных полученных из модели
/// и добавление их в информационную модель
///
///
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();
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**" в экспортированной сводной модели появятся объекты нашей модели.
{{ :developers:tutorial:createmodel:tut_export_model_views_2.png?direct |}}
[[developers:tutorial:tutorialcode|Исходный код]] примера расположен в проекте **"TutorialExportModelViews"**.