====== Теги динамических чертежей профилей ======
В программном комплексе [[http://topomatic.ru/|Топоматик Робур]] чертежи профилей генерируются на основе шаблонов. Шаблон чертежа профиля представляет из себя файл в формате [[https://ru.wikipedia.org/wiki/DXF|DXF (Drawing eXchange Format)]]. При генерации чертежа используются специальные динамические объекты называемые [[road:commons_tasks:archive_sections:format_template_drawing:basic_codes_rail_subobject:start|тегами]]. Теги это элементы [[road:commons_tasks:archive_sections:format_template_drawing_cross_profile:start|шаблона чертежа]], которые представляют необходимую информацию о модели в требуемом графическом виде.
==== Создания пользовательского тега ====
Для создания пользовательского тега необходимы следующие действия:
- Создание класса тега, содержащего алгоритм отрисовки графической информации на чертеже
- Создание класса провайдера тега, отвечающего за регистрацию его в системе
- Объявление команды регистрирующей тег и добавление таска для регистрации тега при инициализации пользовательского модуля
При необходимости, в plugin файле можно объявить action добавления тега в шаблон чертежа
Программно, тег является классом наследником от [[developers:references:topomatic.plt.templates.prf.prffield|PrfField]] для продольного профиля и [[developers:references:topomatic.plt.templates.crs.crsfield|CrsField]] для поперечного профиля. Оба эти класса, в свою очередь, являются наследниками класса [[developers:references:topomatic.plt.templates.common.templatefield|TemplateField]]. Класс тега содержит его свойства и описание алгоритмов построения графических примитивов.
Провайдер тега это класс наследник от [[developers:references:topomatic.plt.templates.templatefieldprovider|TemplateFieldProvider]], с помощью которого мы будем регистрировать наш тег в системе.
==== Подготовка модуля ====
Создайте и настройте новый [[developers:tutorial:module|модуль]] для подключения к программному комплексу [[http://topomatic.ru/|Топоматик Робур]].
С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:
* [[developers:references:topomatic.alg|Topomatic.Alg.dll]] - базовые классы подобъектов
* [[developers:references:topomatic.alg.runtime|Topomatic.Alg.Runtime.dll]] - возможность использования статического класса AlgCoreTools, для доступа к необходимым константам
* [[developers:references:topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции
* [[developers:references:topomatic.cad.view|Topomatic.Cad.View.dll]] - элемент управления для отображения слоёв моделей
* [[developers:references:topomatic.componentmodel|Topomatic.ComponentModel.dll]] - возможность использования статического класса TypeExplorer
* [[developers:references:topomatic.controls|Topomatic.Controls.dll]] - возможность использования нативного диалогового окна программного комплекса Топоматик Робур (Класс [[developers:references:topomatic.controls.dialogs.messagedlg|MessageDlg]]). Данная библиотека не является обязательной, но она требуется для работоспособности кода примеров приведённых далее.
* [[developers:references:topomatic.crs|Topomatic.Crs.dll]] - базовые классы поперечных профилей
* [[developers:references:topomatic.dwg|Topomatic.Dwg.dll]] - работа с чертежами
* [[developers:references:topomatic.plt|Topomatic.Plt.dll]] - шаблоны и теги
==== Добавление графических примитивов ====
Добавление графических примитивов в чертёж ([[developers:references:topomatic.dwg.drawing|Drawing]]) описано в главе руководства "[[developers:tutorial:drawing|Работа с примитивами чертежа]]". Доступ к чертежу ([[developers:references:topomatic.dwg.drawing|Drawing]]) осуществляется с помощью свойства [[developers:references:topomatic.plt.templates.common.templatefield.drawing|Drawing]] унаследованного нашим классом от [[developers:references:topomatic.plt.templates.prf.prffield|PrfField]].
В программном комплексе [[http://topomatic.ru/|Топоматик Робур]] реализован механизм динамических чертежей. Данный механизм позволяет изменять положение примитивов в [[road:commons_tasks:general_information_layouts_drawings:start|макете чертежа]], не теряя при этом связи с исходной моделью. Например, мы можем переместить группу примитивов объединённых одним ключом в макете чертежа. При изменении состояния исходной модели смещение ключа останется.
Например:
- Исходное состояние
{{ :developers:tutorial:alignmentedit:tutdyndrawing1.png?nolink&400 |}}
- После изменения положения группы примитивов объединённых общим ключом
{{ :developers:tutorial:alignmentedit:tutdyndrawing2.png?nolink&400 |}}
- После изменения модели
{{ :developers:tutorial:alignmentedit:tutdyndrawing3.png?nolink&400 |}}
Чтобы положение примитивов можно было изменять в пространстве макета, необходимо воспользоваться следующей конструкцией:
...
var key = GenerateSimpleKey(this, station, "keyName"); // Генерируем динамический ключ для макета
BeginMockup(key, position); // Добавление ключа в макет и привязка примитивов к динамическому ключу
try
{
// В этом блоке нужно добавить примитивы в Drawing
// Все эти примитивы будут привязаны к динамическому ключу
drawing.ActiveSpace.AddText("foobar", position, 2.0, 1.0, 0, 0);
}
finally{ EndMockup(); }
...
Так же можно воспользоваться методом [[developers:references:topomatic.plt.templates.common.templatefield.drawmockuptext|DrawMockupText()]], если необходимо создать текстовый примитив с возможностью изменять его местоположение в макете.
==== Масштабирование координат ====
Чертежи генерируются с учётом масштабов установленных пользователем. Расположение примитивов чертежа должно соответствовать этим масштабам.
=== Продольный профиль ===
В классическом представлении, координатами продольного профиля являются высотная отметка (Y) и пикетажное положение (X). При моделировании трассы, пикетажное положение является значением производным от расстояния до начала трассы. Для преобразования координат продольного профиля следует пользоваться специальными методами:
* [[developers:references:topomatic.plt.templates.prf.prffield.scaleelevation|ScaleElevation()]] - пересчёт высотной отметки в соответствии с вертикальным масштабом чертежа
* [[developers:references:topomatic.plt.templates.prf.prffield.scalestation|ScaleStation()]] - пересчёт расстояния от начала пути в соответствии с горизонтальным масштабом чертежа
=== Поперечный профиль ===
Координатами поперечного профиля являются высотная отметка (Y) и величина смещения от оси базовой трассы (X). Для преобразования координат поперечного профиля следует пользоваться специальными методами:
* [[developers:references:topomatic.plt.templates.crs.crsfield.scaleelevation|ScaleElevation()]] - пересчёт высотной отметки в соответствии с вертикальным масштабом чертежа
* [[developers:references:topomatic.plt.templates.crs.crsfield.scaleoffset|ScaleOffset()]] - пересчёт горизонтального смещения в соответствии с горизонтальным масштабом чертежа
==== Пользовательский тег динамического чертежа продольного профиля ====
В этом примере мы создадим тег для чертежа продольного профиля и зарегистрируем его. Тег будет отрисовывать отметки точек экстремума чёрного и красного профилей.
Создадим класс тега. Так как тег предназначен для продольного профиля, то наследовать наш класс мы будем от [[developers:references:topomatic.plt.templates.prf.prffield|PrfField]]. Добавим нашему классу свойство **Precision**, которое будет определять количество знаков после запятой у отрисовываемых отметок. Свойство следует декорировать атрибутом [[https://docs.microsoft.com/ru-ru/dotnet/api/system.componentmodel.displaynameattribute?view=net-6.0|DisplayNameAttribute]] или его наследником, для получения имени свойства из ресурсов нашей сборки. В нашем случае напишем свой класс наследник [[https://docs.microsoft.com/ru-ru/dotnet/api/system.componentmodel.displaynameattribute?view=net-6.0|DisplayNameAttribute]] для удобства отладки. В конструкторе класса проинициализируем свойство нужным значением.
Алгоритм отрисовки графических и текстовых примитивов описывается в методе [[developers:references:topomatic.plt.templates.prf.prffield.ondrawfield|OnDrawField()]], который необходимо перекрыть. Вернём словарь данных нашего тега через свойство [[developers:references:topomatic.plt.templates.common.templatefield.datamanager|DataManager]]. С его помощью мы получим указатель на активный продольный профиль ([[developers:references:topomatic.alg.prf.transition|Transition]]) по ключевому слову **"ActiveTransition"**. Для нашей задачи потребуются линии черного и красного профилей, которые мы получим через свойства [[developers:references:topomatic.alg.prf.transition.egprofile|EgProfile]] и [[developers:references:topomatic.alg.prf.transition.redprofile|RedProfile]] соответственно у активного профиля. Подробнее о классе [[developers:references:topomatic.alg.prf.transition|Transition]] можно узнать в разделе руководства "[[developers:tutorial:algedit|Редактирование плана и профиля]]" в подразделе **Редактирование профиля**.
Отметку и расстояние узла профиля можно получить с помощью свойств [[developers:references:topomatic.alg.prf.profilenode.elevation|Elevation]] и [[developers:references:topomatic.alg.prf.profilenode.station|Station]] соответственно.
В процессе вычислений, нам необходимо проверять, попадает ли текущий узел профиля в границы чертежа, чтобы тег не производил отрисовку примитивов за его вне границ и не тратил время на избыточные вычисления. Чтобы понять, находимся ли мы в границах чертежа, следует воспользоваться методом [[developers:references:topomatic.alg.alignmentvalueconverter.stationinlimits_system.double_system.double_system.double_system.boolean_system.boolean|StationInLimits()]] статического класса [[developers:references:topomatic.alg.alignmentvalueconverter|AlignmentValueConverter]]. В качестве аргументов метод принимает проверяемое расстояние от начала пути, а так же расстояния начала и конца границы чертежа. Значение расстояний начала и конца границ чертежа можно получить с помощью свойств [[developers:references:topomatic.plt.templates.prf.prffield.startsta|StartSta]] и [[developers:references:topomatic.plt.templates.prf.prffield.endsta|EndSta]] унаследованных нашим классом от [[developers:references:topomatic.plt.templates.prf.prffield|PrfField]].
Добавьте в программу новый класс со следующим содержанием:
...
class TutorialProfileField : PrfField
{
private int _precision;
///
/// Количество знаков после запятой
///
[SRDisplayName("sTutorialFieldPrecisionProperty")]
public int Precision
{
get => _precision;
set => _precision = value > 0 ? value : 0;
}
public TutorialProfileField() : base()
{
Precision = 2;
}
protected override void OnDrawField()
{
base.OnDrawField();
// Получаем активную ось профиля и её линии земли и проектного профиля
var transition = this.DataManager["ActiveTransition"] as Transition;
if (transition == null) return;
var drawing = Drawing;
var egProfile = transition.EgProfile;
var redProfile = transition.RedProfile;
// Рассчитываем и отрисовываем минимальные и максимальные отметки линий земли и проектного профиля
if (egProfile.Count > 0)
{
var minEgElev = double.MaxValue;
var minEgElevSta = 0.0;
var maxEgElev = double.MinValue;
var maxEgElevSta = 0.0;
for (var i = 0; i < egProfile.Count; i++)
{
var node = egProfile[i];
// Проверяем, попадает ли узел в текущий разрыв
if (!AlignmentValueConverter.StationInLimits(node.Station, StartSta, EndSta, true, true))
continue;
if (node.Elevation < minEgElev)
{
minEgElev = node.Elevation;
minEgElevSta = node.Station;
}
if (node.Elevation > maxEgElev)
{
maxEgElev = node.Elevation;
maxEgElevSta = node.Station;
}
}
var minEgEnt = DrawElevation(drawing, minEgElev, minEgElevSta, CadColor.Green);
if (ValueConverter.CompValues(minEgElev, maxEgElev) != 0)
{
var maxEgEnt = DrawElevation(drawing, maxEgElev, maxEgElevSta, CadColor.Green);
minEgEnt.Content = $"E(L): {minEgEnt.Content}";
maxEgEnt.Content = $"E(H): {maxEgEnt.Content}";
}
}
if (redProfile.Count > 0)
{
var minRedElev = double.MaxValue;
var minRedElevSta = 0.0;
var maxRedElev = double.MinValue;
var maxRedElevSta = 0.0;
foreach (var node in redProfile)
{
if (node.Elevation < minRedElev)
{
minRedElev = node.Elevation;
minRedElevSta = node.Station;
}
if (node.Elevation > maxRedElev)
{
maxRedElev = node.Elevation;
maxRedElevSta = node.Station;
}
}
var minRedEnt = DrawElevation(drawing, minRedElev, minRedElevSta, CadColor.Red);
if (ValueConverter.CompValues(minRedElev, maxRedElev) != 0)
{
var maxRedEnt = DrawElevation(drawing, maxRedElev, maxRedElevSta, CadColor.Red);
minRedEnt.Content = $"R(L): {minRedEnt.Content}";
maxRedEnt.Content = $"R(H): {maxRedEnt.Content}";
}
}
}
///
/// Отрисовка текстового примитива
///
///
///
///
///
private DwgText DrawElevation(Drawing drawing, double value, double station, CadColor color)
{
var textStyle = drawing.ActiveStyle;
// Рассчитываем положение примитива с учётом масштаба макета
var scaleStation = ScaleStation(station);
var position = new Vector3D(scaleStation, 0.0, 0.0);
// Генерируем динамический ключ для макета и создаём примитив
var key = GenerateSimpleKey(this, scaleStation, "elevation");
BeginMockup(key, position.Pos);
try
{
var ent = drawing.ActiveSpace.AddText(ValueConverter.FloatToStr(value, Precision), position, textStyle.Height, textStyle.Ratio, Math.PI * 0.5, textStyle.Oblique);
ent.Color = color;
ent.Justify = this.DefaultTextJustify;
return ent;
}
finally{ EndMockup(); }
}
}
...
Теперь необходимо создать провайдер, главной функцией которого будет регистрация нашего тега. Провайдер должен наследоваться от класса [[developers:references:topomatic.plt.templates.templatefieldprovider|TemplateFieldProvider]] и перекрывать метод [[developers:references:topomatic.plt.templates.templatefieldprovider.provide_topomatic.plt.templates.common.templateprocessor|Provide()]]. Провайдер регистрирует тег с помощью метода [[developers:references:topomatic.plt.templates.common.templateprocessor.registerfieldalias_system.string_system.string|RegisterFieldAlias()]] обработчика шаблонов ([[developers:references:topomatic.plt.templates.common.templateprocessor|TemplateProcessor]]). Этот метод вернёт дескриптор зарегистрированного тега, в который следует добавить свойства, которые в последствии будут отображаться на панели свойств во время редактирования шаблона чертежа.
Добавьте в программу новый класс со следующим содержанием:
...
class TutorialProfileFieldProvider : TemplateFieldProvider
{
public override void Provide(TemplateProcessor templateProcessor)
{
// Регистрация тега продольного профиля
var profileFieldDescriptor = templateProcessor.RegisterFieldAlias(
"TutorialProfileField_MaxMinElevation",
TypeExplorer.GetSerializableString(typeof(TutorialProfileField)),
Resources.sTutorialProfileField);
profileFieldDescriptor.Add("Precision");
}
}
...
{{ :developers:tutorial:alignmentedit:tutcustomprffield.png?nolink |}}
==== Пользовательский тег динамического чертежа поперечного профиля профиля ====
В этом примере мы создадим тег для чертежа поперечного профиля и зарегистрируем его. Конструктивно, создание тега поперечного профиля и тега продольного профиля во многом идентично. Аналогично примеру для продольного профиля, тег будет отрисовывать отметки точек экстремума контура сечения земли и контура проектной конструкции.
Создадим класс тега. Так как тег предназначен для поперечного профиля, то наследовать наш класс мы будем от [[developers:references:topomatic.plt.templates.crs.crsfield|CrsField]]. Добавим нашему классу свойство **Precision**, которое будет определять количество знаков после запятой у отрисовываемых отметок. Свойство следует декорировать атрибутом [[https://docs.microsoft.com/ru-ru/dotnet/api/system.componentmodel.displaynameattribute?view=net-6.0|DisplayNameAttribute]] или его наследником, для получения имени свойства из ресурсов нашей сборки. В нашем случае напишем свой класс наследник [[https://docs.microsoft.com/ru-ru/dotnet/api/system.componentmodel.displaynameattribute?view=net-6.0|DisplayNameAttribute]] для удобства отладки. В конструкторе класса проинициализируем свойство нужным значением.
Алгоритм отрисовки графических и текстовых примитивов описывается в методе [[developers:references:topomatic.plt.templates.prf.crsfield.ondrawfield|OnDrawField()]], который необходимо перекрыть. Вернём словарь данных нашего тега через свойство [[developers:references:topomatic.plt.templates.common.templatefield.datamanager|DataManager]]. С его помощью мы получим указатель на базовый подобъект ([[developers:references:topomatic.alg.alignment|Alignment]]) по ключевому слову **"Alignment"**. Для нашей задачи потребуются контур сечения земли и контур проектной конструкции, которые мы получим из контекста конструирования поперечного профиля ([[developers:references:topomatic.crs.templates.crsdesigncontext|CrsDesignContext]]). Подробнее о классе [[developers:references:topomatic.crs.templates.crsdesigncontext|CrsDesignContext]] и контурах поперечного профиля можно узнать в разделе руководства "[[developers:tutorial:crsselection|Выбор объектов на поперечном профиле]]" в подразделе **Контекст конструирования поперечного профиля**.
Индекс поперечного профиля получается с помощью свойства [[developers:references:topomatic.plt.templates.crs.crsfield.sectionindex|SectionIndex]] унаследованного от [[developers:references:topomatic.plt.templates.crs.crsfield|CrsField]]. Получим контекст конструирования поперечного профиля по индексу поперечного профиля с помощью свойства [[developers:references:topomatic.alg.crs.corridor.item_system.int32|Item[Int32]]] класса [[developers:references:topomatic.alg.crs.corridor|Corridor]] (подробнее о получении [[developers:references:topomatic.alg.crs.corridor|Corridor]] можно узнать из раздела руководства "[[developers:tutorial:algstationing|Выбор модели подобъекта и преобразование координат]]").
Добавьте в программу новый класс со следующим содержанием:
...
class TutorialCrossectionField : CrsField
{
private int _precision;
///
/// Количество знаков после запятой
///
[SRDisplayName("sTutorialFieldPrecisionProperty")]
public int Precision
{
get => _precision;
set => _precision = value > 0 ? value : 0;
}
public TutorialCrossectionField() : base()
{
Precision = 2;
}
protected override void OnDrawField()
{
base.OnDrawField();
// Получаем активную ось профиля и её линии земли и конструкции проектного поперечника
var alignment = DataManager["Alignment"] as Alignment;
if (alignment == null) return;
var drawing = Drawing;
var corridor = alignment.Corridor;
var sectionId = this.SectionIndex;
var context = corridor[sectionId];
var egContour = context.GetEgContour();
var redContour = context.GetRedLineContour();
// Рассчитываем и отрисовываем минимальные и максимальные отметки линий земли и конструкции проектного поперечника
if (egContour.Count > 0)
{
var minEgElev = double.MaxValue;
var minEgElevOffset = 0.0;
var maxEgElev = double.MinValue;
var maxEgElevOffset = 0.0;
for (var i = 0; i < egContour.Count; i++)
{
var node = egContour[i];
if (node.Y < minEgElev)
{
minEgElev = node.Y;
minEgElevOffset = node.X;
}
if (node.Y > maxEgElev)
{
maxEgElev = node.Y;
maxEgElevOffset = node.X;
}
}
var minEgEnt = DrawElevation(drawing, minEgElev, minEgElevOffset, CadColor.Green);
if (ValueConverter.CompValues(minEgElev, maxEgElev) != 0)
{
var maxEgEnt = DrawElevation(drawing, maxEgElev, maxEgElevOffset, CadColor.Green);
minEgEnt.Content = $"E(L): {minEgEnt.Content}";
maxEgEnt.Content = $"E(H): {maxEgEnt.Content}";
}
}
if (redContour.Count > 0)
{
var minRedElev = double.MaxValue;
var minRedElevOffset = 0.0;
var maxRedElev = double.MinValue;
var maxRedElevOffset = 0.0;
for (var i = 0; i < redContour.Count; i++)
{
var node = redContour[i];
if (node.Y < minRedElev)
{
minRedElev = node.Y;
minRedElevOffset = node.X;
}
if (node.Y > maxRedElev)
{
maxRedElev = node.Y;
maxRedElevOffset = node.X;
}
}
var minRedEnt = DrawElevation(drawing, minRedElev, minRedElevOffset, CadColor.Red);
if (ValueConverter.CompValues(minRedElev, maxRedElev) != 0)
{
var maxRedEnt = DrawElevation(drawing, maxRedElev, maxRedElevOffset, CadColor.Red);
minRedEnt.Content = $"R(L): {minRedEnt.Content}";
maxRedEnt.Content = $"R(H): {maxRedEnt.Content}";
}
}
}
///
/// Отрисовка текстового примитива
///
///
///
///
///
private DwgText DrawElevation(Drawing drawing, double value, double offset, CadColor color)
{
var textStyle = drawing.ActiveStyle;
// Рассчитываем положение примитива с учётом масштаба макета
var scaleOffset = ScaleOffset(offset);
var position = new Vector3D(scaleOffset, 0.0, 0.0);
// Генерируем динамический ключ для макета и создаём примитив
var key = GenerateSimpleKey(this, scaleOffset, 0);
BeginMockup(key, position.Pos);
try
{
var ent = drawing.ActiveSpace.AddText(ValueConverter.FloatToStr(value, Precision), position,
textStyle.Height, textStyle.Ratio, Math.PI * 0.5, textStyle.Oblique);
ent.Color = color;
ent.Justify = this.DefaultTextJustify;
return ent;
}
finally
{
EndMockup();
}
}
}
...
Провайдер тега поперечного профиля формируется так же, как и для тега продольного профиля. Добавьте в программу новый класс со следующим содержанием:
...
internal class TutorialCrossectionFieldProvider : TemplateFieldProvider
{
public override void Provide(TemplateProcessor templateProcessor)
{
// Регистрация тега поперечного профиля
var crossectionFieldDescriptor = templateProcessor.RegisterFieldAlias(
"TutorialCrossectionField_MaxMinElevation",
TypeExplorer.GetSerializableString(typeof(TutorialCrossectionField)),
Resources.sTutorialCrossectionField);
crossectionFieldDescriptor.Add("Precision");
}
}
...
{{ :developers:tutorial:alignmentedit:tutcustomcrsfield.png?nolink |}}
==== Регистрация тегов ====
Теги следует регистрировать в момент инициализации пользовательского модуля. Для этого в теле программного модуля объявите команды, и декорируйте её атрибутом «cmd». Команды "provide_tutorial_profile_fields" и "provide_tutorial_crossection_fields" будут создавать провайдеры соответствующих типов и выполнять регистрацию тегов.
...
// Команда вызова процесса регистрации тега продольного профиля
[cmd("provide_tutorial_profile_fields")]
private void ProvideProfileFields(TemplateProcessor templateProcessor)
{
var provider = new TutorialProfileFieldProvider();
provider.Provide(templateProcessor);
}
// Команда вызова процесса регистрации тега поперечного профиля
[cmd("provide_tutorial_crossection_fields")]
private void ProvideCrossectionFields(TemplateProcessor templateProcessor)
{
var provider = new TutorialCrossectionFieldProvider();
provider.Provide(templateProcessor);
}
...
Остаётся зарегистрировать соответствующие таски. Для этого в классе наследнике от [[developers:references:topomatic.applicationplatform.plugins.pluginhostinitializator|PluginHostInitializator]] нашего модуля следует перекрыть метод [[developers:references:topomatic.applicationplatform.plugins.pluginhostinitializator.initialize|Initialize()]] и добавить в него соответствующие инструкции. Пример такого класса будет выглядеть так:
...
public class ModulePluginHost : PluginHostInitializator
{
protected override Type[] GetTypes()
{
return new Type[] { typeof(Module) };
}
public override void Initialize(PluginFactory factory)
{
base.Initialize(factory);
factory.RegisterTask(AlgCoreTools.TASK_PLT_FIELDS,
$"{TemplateDwgGenerator.ID_PRF_RAIL}:provide_tutorial_profile_fields");
factory.RegisterTask(AlgCoreTools.TASK_PLT_FIELDS,
$"{TemplateDwgGenerator.ID_CRS_RAIL}:provide_tutorial_crossection_fields");
}
}
...
==== Action добавления тега в шаблон чертежа ====
Редактировать шаблон чертежа можно изменяя содержимое текстовых примитивов посредством DXF-редактора, но более удобно воспользоваться редактором шаблонов программного комплекса Топоматик Робур.
Для добавление тега в редакторе шаблонов нужно создать новый **action** в **plugin** файле. **Action** должен вызывать команду в формате
"insert_template_field \"<Выравнивание> <Индекс_подобъекта> <Идентификатор_тега> <Атрибуты_через_пробел>\""
В шаблоне чертежа тег является текстовым примитивом, содержание которого представлено в формате "**$<Имя_тега>**". Так же после имени тега, в тексте могут содержаться атрибуты разделённые пробелами. Атрибуты определяют значения свойств тега по порядку. Порядок свойств тега соответствует порядку свойств добавленных при регистрации тега.
Механизм построения чертежа определяет все текстовые примитивы с вышеописанным содержанием. Далее эти примитивы заменяются на наборы примитивов, построенные подпрограммами соответствующих тегов.
Теперь необходимо сформировать наш файл .plugin. Заполните его следующим образом.
{
"assemblies": {
"TutorialFields": {
"assembly": "TutorialFields.dll, TutorialFields.ModulePluginHost"
}
},
"actions": {
"id_insert_tutorial_profile_tag": {
"cmd": "insert_template_field \"MiddleCenter 0 TutorialProfileField_MaxMinElevation 0;1\"",
"title": "Мой тег продольного профиля",
"description": "Мой тег продольного профиля",
"flags": "$(prf_rail_field)"
},
"id_insert_tutorial_crossection_tag": {
"cmd": "insert_template_field \"MiddleCenter 0 TutorialCrossectionField_MaxMinElevation\"",
"title": "Мой тег поперечного профиля",
"description": "Мой тег поперечного профиля",
"flags": "$(crs_rail_field)"
}
},
"menubars": {
"rbproj": {
"items": [
{
"id": "tutorial_menu",
"title": "Tutorial",
"items": [
"id_insert_tutorial_profile_tag",
"id_insert_tutorial_crossection_tag"
]
}
]
}
}
}
Результатом запуска проекта будет появление в главном меню пункта «Tutorial», с подпунктами, которые будут работать в соответствии с описанными выше алгоритмами.
[[developers:tutorial:tutorialcode|Исходный код]] примера расположен в проекте **"TutorialFields"**.