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

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


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

developers:tutorial:algedit

Редактирование плана и профиля

Редактирование плана

План линии (или план трассы) является экземпляром класса PlanLine. Получить план линии можно с помощью свойства Plan класса Alignment (описание класса Alignment представлено в предыдущем разделе руководства Выбор модели подобъекта и преобразование координат). План линии представляет из себя упорядоченную коллекцию вершин плана линии (Vertex). Изменение плана линии осуществляется с помощью следующих методов класса PlanLine:

  • Add() - добавление новой вершины в конец плана линии
  • Clear() - удаление всех вершин плана линии
  • Insert() - вставка новой вершины в определённое место в описании плана линии
  • Remove() - удаление вершины плана линии
  • RemoveAt() - удаление вершины плана линии по её индексу

Вершины плана линии

Вершина плана линии является экземпляром класса Vertex. Она представляет из себя упорядоченную коллекцию элементов вершин плана линии (VertexItem), а также хранит в себе следующую информацию о её состоянии:

  • Beta - угол поворота вершины плана линии в радианах
  • P - расстояние от начала трассы до вершины плана линии
  • PlanData - геометрические показатели вершины плана линии
  • Position - плановое положение вершины плана линии

Вершины плана линии создаются с помощью конструктора класса Vertex() и добавляется в PlanLine. Изменение коллекции элементов вершин осуществляется методами аналогично тем, что описаны выше для работы с планом линии.

Если вершина не содержит ни одного элемента, то она является изломом плана линии. Если вершина содержит только один элемент, то она представляет из себя однорадиусную кривую. Если элементов больше одного, то это многорадиусная кривая.

Элемент вершины плана линии

Элемент вершины плана линии являются структурами VertexItem. Непосредственное изменение VertexItem не приведёт к изменению плана линии. То есть, если вы хотите изменить существующий элемент вершины плана линии, то вам необходимо заменить существующий элемент вершины аналогично тому, как это осуществляется при работе с массивами. Рассмотрим пример изменения радиуса существующего элемента вершины плана линии.

var vertex = Alignment.Plan[i]; // получение вершины плана линии
var item = vertex[j]; // получение элемента вершины плана линии
item.R = 500; // изменение радиуса вершины плана линии
vertex[j] = item; // замена существующего элемента вершины плана линии на изменённый

Элемент вершины плана линии создаётся с помощью конструктора VertexItem() и добавляется в Vertex. Состояние элемента вершины плана линии описывается следующими полями:

  • K - доля угла вершины плана линии (Vertex.Beta)
  • L1 - входная переходная кривая
  • L2 - выходная переходная кривая
  • R - радиус круговой кривой

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

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

С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:

Для получения текущей модели подобъекта воспользуемся ресивером текущего подобъекта через метод CreateReciver() статического класса ActiveAlignmentReciver. Свойство Alignment ресивера вернёт Alignment текущего подобъекта или последнего активного подобъекта, если в данный момент активна модель отличная от подобъекта и активирует её.

Добавление элемента вершины плана линии в однорадиусную кривую

В этом примере укажем точку на плане, определим ближайшую к этой точке вершину плана линии и если это вершина является однорадиусной кривой, то добавим к ней новый элемент вершины плана линии.

Укажем точку на плане и определим расстояние от начала трассы до этой точки аналогично тому, как описано в предыдущем разделе руководства Выбор модели подобъекта и преобразование координат. Далее определим ближайшую к этой точке вершину плана линии с помощью метода SearchNearest() класса PlanLine и если у эта вершина содержит один элемент, то добавим в неё ещё один.

С целью недопущения нарушения геометрии плана линии, изменению вершины плана линии должен предшествовать вызов метода BeginTransaction() у этой вершины. Внесённые изменения следует проверить на нарушение геометрии плана с помощью метода PlanVertexesValid() статического класса PlanLineSolver, передав ему PlanLine. Если проверка прошла успешно, то изменения вершины фиксируются вызовом метода Commit(). В противном случае изменения необходимо откатить методом Rollback(). Сами изменение следует производить в блоке try/finally.

В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда «add_vertex_to_alignment» предложит пользователю указать точку на плане, определит ближайшую вершину плана линии и если эта вершина содержит только один элемент, то в вершину будет добавлен новый элемент.

...
    [cmd("add_vertex_to_alignment")]
    private void AddVertexToAlignment()
    {
        //Получаем модель текущей трассы. Если текущей является не трасса, то активируется последняя активная трасса
        Alignment alignment;
        using (var receiver = ActiveAlignmentReciver<Alignment>.CreateReciver(false))
        {
            alignment = receiver.Alignment;
            if (alignment == null)
            {
                MessageDlg.Show("Последний активный подобъект не найден.", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                return;
            }
 
            var cadview = CadView;
            if (cadview == null) return;
 
            var plan = alignment.Plan;
            var compound = plan.CompoundLine;
 
            //Указываем однорадиусную кривую
            Vector3D point;
            var getPointRes = CadCursors.GetPoint(cadview, out point, "Укажите однорадиусную кривую:");
            if (!getPointRes) return;
 
            double station, offset;
            var res = compound.PosToStaOffset(point.Pos, out station, out offset);
            if (!res)
            {
                MessageDlg.Show("Указанная точка лежит вне плана линии.", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                return;
            }
 
            //Получаем ближайшую вершину угла к указанной точке и добавляем в неё элемент
            var vertex = plan.SearchNearest(station);
 
            if (vertex.Count != 1)
            {
                MessageDlg.Show("Необходимо указать однорадиусную кривую.", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                return;
            }
 
            vertex.BeginTransaction();
            var result = true;
            try
            {
                var angle = Math.Abs(vertex.Beta);
                var item1 = vertex[0];
                var item2 = new PlanLine.Vertex.VertexItem { R = item1.R, L2 = item1.L2, K = angle * 0.5 };
                item1.L2 = 0.0;
                item1.K = angle * 0.5;
                vertex[0] = item1;
                vertex.Add(item2);
                result = PlanLineSolver.PlanVertexesValid(plan);
            }
            finally
            {
                if (result)
                {
                    vertex.Commit();
                }
                else
                {
                    vertex.Rollback();
                    MessageDlg.Show("При разбивке кривой произошла ошибка.", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                }
            }
        }
    }
...

Редактирование профиля

Профиль является наследником класса Transition. Доступ к коллекции профилей подобъекта осуществляется через свойство Transitions класса Alignment. Коллекция профилей содержит профиль по оси, а также восемь профилей слева и восемь профилей справа. Наиболее часто используемый профиль по оси трассы расположен в начале коллекции и имеет индекс 0.

Каждый профиль содержит в себе информацию о чёрном профиле, красном профиле и интерполированном профиле. Каждый из этих профилей получается с помощью соответствующего свойства класса Transition:

Каждый из описанных выше профилей содержит в себе узлы профиля соответствующие его типу. Для интерполированного профиля узлы являются структурами AgProfileNode, для чёрного профиля ProfileNode, а для красного профиля ProjectNode. За сортировку узлов в профиле отвечает тот, кто заполняет профиль, это необходимо для корректной обработки вертикальных стенок и обратных уклонов земли.

Если в чёрный профиль требуется внести изменения, то необходимо сделать его статическим. Для этого нужно свойству IsDynamicEarth класса Transition установить значение false и получить существующий профиль через свойство StaticEg.

Статический профиль не обновляет своё состояние в случае изменений ЦММ на которые ссылается подобъект профиля.

Изменение коллекций узлов профилей выполняется аналогично работе с массивами.

Сглаживание вершины красного профиля

В этом примере мы выберем вершину красного профиля и сгладим её путём удаления существующей вершины и добавления двух новых с заранее рассчитанными уклонами на заданном расстоянии друг от друга.

Аналогично предыдущему примеру, определим активную модель подобъекта. Рамкой укажем область в которой располагается вершина красного профиля с помощью метода GetFrame() класса CadCursors. Полученные значения координат необходимо переконвертировать из координат экрана в координаты профиля. Конвертация осуществляется методом UnProjectBox() класса CadView. Далее определим какая вершина профиля попадает в границы указанных координат. Для этого следует воспользоваться методом Contains() структуры BoundingBox2D и передать в него положение узла красного профиля.

Определив необходимую вершину, рассчитаем положение новых вершин и внесём изменения в проектный профиль.

В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда «smooth_peak» предложит пользователю выбрать рамкой вершину красного профиля и указать расстояние между новыми сглаживающими вершинами.

...
    [cmd("smooth_peak")]
    private void SmoothPeak()
    {
        //Получаем модель текущей трассы. Если текущей является не трасса, то активируется последняя активная трасса
        Alignment alignment;
        using (var receiver = ActiveAlignmentReciver<Alignment>.CreateReciver(false))
        {
            alignment = receiver.Alignment;
            if (alignment == null)
            {
                MessageDlg.Show("Необходимо сделать трассу активной.", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                return;
            }
 
            var cadview = CadView;
            if (cadview == null) return;
 
            //Выбираем рамкой вершину красного профиля, которую требуется сгладить
            RectangleD rectangle;
            FrameSelectType ft;
            if (CadCursors.GetFrame(cadview, out rectangle, out ft, "Выберите вершину:") != GetPointResult.Accept)
                return;
            var bounds = rectangle.ToBoundingBox();
            bounds = cadview.UnProjectBox(bounds);
 
            //Получаем красный профиль и находим указанную вершину на этом профиле
            var profile = alignment.Transitions[0].RedProfile;
            for (int i = 1; i < profile.Count - 1; i++)
            {
                var curNode = profile[i];
 
                //Если это выбранная вершина, то предлагаем пользователю ввести длину сглаживающего сегмента,
                //высчитываем положение новых вершин, добавляем их в профиль и удаляем исходную вершину
                if (bounds.Contains(curNode.Position) != ContainmentType.Disjoint)
                {
                    var length = 0.0;
                    var res = CadCursors.GetDouble(cadview, ref length, "Укажите длину сглаживающего сегмента:");
                    if (res != GetPointResult.Accept) return;
 
                    var prevNode = profile[i - 1];
                    var nextNode = profile[i + 1];
 
                    var prevLength = curNode.Station - prevNode.Station;
                    var curLength = nextNode.Station - curNode.Station;
 
                    if (prevLength < length * 0.5 || curLength < length * 0.5)
                    {
                        MessageDlg.Show("Необходимо указать меньшее значение длины сегмента сглаживания.", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
                        return;
                    }
 
                    var prevIncline = (curNode.Elevation - prevNode.Elevation) / prevLength;
                    var curIncline = (nextNode.Elevation - curNode.Elevation) / curLength;
 
                    var startStation = prevNode.Station + (prevLength - length * 0.5);
                    var startElevation = prevNode.Elevation + prevIncline * (prevLength - length * 0.5);
                    var startNode = new ProjectNode(startStation, startElevation, 0, 0, ProjectNodeFlags.UseRadius);
 
                    var endStation = curNode.Station + length * 0.5;
                    var endElevation = curNode.Elevation + curIncline * length * 0.5;
                    var endNode = new ProjectNode(endStation, endElevation, 0, 0, ProjectNodeFlags.UseRadius);
 
                    profile.BeginUpdate();
                    try
                    {
                        profile.Add(startNode);
                        profile.Add(endNode);
                        profile.Remove(curNode);
                    }
                    finally
                    {
                        profile.EndUpdate();
                    }
 
                    return;
                }
            }
        }
    }
...

Теперь необходимо сформировать наш файл .plugin. Заполните его следующим образом.

{
  "assemblies": {
    "TutorialEditAlignment": {
      "assembly": "TutorialEditAlignment.dll, TutorialEditAlignment.ModulePluginHost"
    }
  },
 
  "actions": {
    "id_add_vertex_to_alignment": {
      "cmd": "add_vertex_to_alignment",
      "title": "Добавить вершину в план линии"
    },
    "id_smooth_peak": {
      "cmd": "smooth_peak",
      "title": "Сгладить вершину профиля"
    }
  },
 
  "menubars": {
    "rbproj": {
      "items": [
        {
          "id": "tutorial_menu",
          "title": "Tutorial",
          "items": [
            "id_add_vertex_to_alignment",
            "id_smooth_peak"
          ]
        }
      ]
    }
  }
}

Результатом запуска проекта будет появление в главном меню пункта «Tutorial», с подпунктами, которые будут работать в соответствии с описанными выше алгоритмами.

Исходный код примера расположен в проекте «TutorialEditAlignment».
developers/tutorial/algedit.txt · Последние изменения: 2022/07/12 21:18 — proxor