Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.13. Вызов функций блоков

§2.13.4. Пример использования функций блоков для поиска пути в графе

Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока-узла графа, способного находить и показывать кратчайший путь между двумя заданными узлами.

      // Использование функций блоков для поиска пути в графе
      #include <windows.h>
      #include <math.h>
      #include <RdsDef.h>
      // Подготовка описаний сервисных функций
      #define RDS_SERV_FUNC_BODY GetInterfaceFunctions
      #include <RdsFunc.h>

      //========== Главная функция DLL ==========
      int WINAPI DllMain(HINSTANCE /*hinst*/,
                               unsigned long reason,
                               void* /*lpReserved*/)
      { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL
          { // Получение доступа к функциям RDS
            if(!GetInterfaceFunctions())
              RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS
          }
        return 1;
      }
      //========= Конец главной функции =========

      //=========================================
      // Функции общего назначения
      //=========================================

      // Вычисление длины кривой Безье численным интегрированием
      // (метод Гаусса)
      double BezierLengthGauss(double x1,double y1,double dx1,double dy1,
          double x2,double y2,double dx2,double dy2,double delta)
      { double ax,bx,cx,ay,by,cy;
        double a,b,c,d,e,q,n,k,T,l,h,I,w;
        int m;
        double x[3],s[3];

        // Вычисление коэффициентов параметрического вида
        ax=2*(x1-x2)+3*(dx1-dx2);
        bx=3*(x2-x1+dx2-2*dx1);
        cx=3*dx1;
        ay=2*(y1-y2)+3*(dy1-dy2);
        by=3*(y2-y1+dy2-2*dy1);
        cy=3*dy1;

        // Интегрирование
        b=1.0;
        n=delta*60.0; m=1; k=0.0;
        do
          { m*=2; a=0.0; T=sqrt(0.6); I=0.0;
            h=(b-a)/m;
            for(int j=1;j<=m;j++)
              { w=a+h;
                c=(w+a)/2; d=(w-a)/2; e=d*5.0/9.0;
                l=d*8.0/9.0; d*=T;
                x[0]=c-d; x[1]=c; x[2]=c+d;
                s[0]=e;   s[1]=l; s[2]=e;
                for(int i=0;i<3;i++)
                  { // Вычисление подынтегральной функции (3 раза)
                    double vx=3*ax*x[i]*x[i]+2*bx*x[i]+cx,
                           vy=3*ay*x[i]*x[i]+2*by*x[i]+cy;
                    double f=sqrt(vx*vx+vy*vy);
                    I+=s[i]*f;
                  }
                a=w;
              }
            l=k; k=I;
          }
        while(fabs(I-l)>n);
        return I;
      }
      //=========================================

      // Определение расстояния между точкой связи и геометрическим
      // центром блока по структуре RDS_POINTDESCRIPTION
      double DistanceFromBlockCenterToPoint(RDS_PPOINTDESCRIPTION point)
      { RDS_BLOCKDIMENSIONS dim; // Структура описания размеров блока
        double dx,dy,xc,yc,xp,yp;

        if(point==NULL) // Указатель не передан - ошибка
          return -1.0;
        // Проверка – точка ли соединения с блоком передана?
        if(point->PointType!=RDS_PTBLOCK)
          return -1.0;

        // Определение размеров блока point->Block
        dim.servSize=sizeof(dim); // Размер структуры
        if(!rdsGetBlockDimensionsEx(point->Block,&dim,RDS_GBD_NONE))
          return -1.0; // Не удалось получить размеры блока

        // Геометрический центр изображения блока
        xc=dim.Left+dim.Width/2.0;
        yc=dim.Top+dim.Height/2.0;
        // Абсолютные координаты точки связи
        xp=dim.BlockX+point->x;
        yp=dim.BlockY+point->y;
        // Вычисление расстояния между этими точками
        dx=xp-xc; dy=yp-yc;
        return sqrt(dx*dx+dy*dy);
      }
      //=========================================

      // Проверка связи (должно быть два блока на концах) и вычисление
      // длины дуги графа, соответствующей этой связи
      double CalcArcLength(RDS_CHANDLE Conn)
      { RDS_CONNDESCRIPTION ConnDescr;   // Структура описания связи
        RDS_POINTDESCRIPTION PointDescr; // Структура описания точки
        RDS_LINEDESCRIPTION LineDescr;   // Структура описания линии
        int BlockCnt;
        double len=0.0; // Общая длина дуги
        double x1,y1,x2,y2;

        // Заполнение служебных полей размеров структур
        ConnDescr.servSize=sizeof(ConnDescr);
        PointDescr.servSize=sizeof(PointDescr);
        LineDescr.servSize=sizeof(LineDescr);

        // Получаем описание связи – нам нужно число точек и линий в ней
        if(!rdsGetConnDescription(Conn,&ConnDescr))
          return -1.0;

        if(ConnDescr.ConnType!=RDS_CTCONNECTION)
          return -1.0; // Это не связь, а шина – шины нам не годятся

        // Проверяем число блоков на концах связи (должно быть ровно 2)
        BlockCnt=0;
        for(int i=0;i<ConnDescr.NumPoints;i++)
          { // Получаем описание точки связи i
            rdsGetPointDescription(Conn,i,&PointDescr);
            // Проверяем тип точки
            switch(PointDescr.PointType)
              { case RDS_PTBUS: // Соединение с шиной – связь не годится
                  return -1.0;
                case RDS_PTBLOCK: // Соединение с блоком
                  BlockCnt++;
                  if(BlockCnt>2) // Связь разветвлена
                    return -1.0;
                  // Найдена точка соединения с блоком. Добавляем к len
                  // расстояние между точкой и центром блока
                  len+=DistanceFromBlockCenterToPoint(&PointDescr);
                  break;
              } // switch(PointDescr.PointType)
          } // for(int i=0;...)
        if(BlockCnt!=2) // Связь соединяет менее двух блоков
         return -1.0;

        // Связь соединена ровно с двумя блоками – суммируем длину всех
        // ее линий
        for(int i=0;i<ConnDescr.NumLines;i++)
          { // Получаем описание линии связи i
            rdsGetLineDescription(Conn,i,&LineDescr,NULL,NULL);
            // Переводим целые координаты концов линии в double
            // для большей точности вычисления
            x1=LineDescr.x1; y1=LineDescr.y1;
            x2=LineDescr.x2; y2=LineDescr.y2;
            // В зависимости от типа линии, вычисляем ее длину и
            // добавляем к len
            switch(LineDescr.LineType)
              { case RDS_LNLINE: // Отрезок прямой
                  len+=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
                  break;
                case RDS_LNBEZIER: // Кривая Безье
                  len+=BezierLengthGauss(x1,y1,
                         LineDescr.dx1,LineDescr.dy1,
                         x2,y2,
                         LineDescr.dx2,LineDescr.dy2,
                         2);
                  break;
              } // switch(LineDescr.LineType)
          } // for(int i=0;...)
        return len;
      }
      //=========================================

      // Визуально выделить связь
      void MarkConnection(RDS_CHANDLE Conn)
      { // Определяем число альтернативных внешних видов
        int StylesCount=rdsAltConnAppearanceOp(Conn,RDS_CAOCOUNT,0,NULL);

        if(StylesCount<1)
          { // Для связи еще не определено ни одного внешнего вида
            // Создаем внешний вид 0 и делаем его толще текущего
            RDS_CONNAPPEARANCE style; // Структура описания стиля связи
            // Получаем описание текущего внешнего вида связи
            style.servSize=sizeof(style);
            rdsGetConnAppearance(Conn,&style);
            // Увеличиваем толщину и размер стрелки
            style.LineWidth*=3;
            style.ArrowLength*=2;
            style.ArrowWidth*=3;
            // Запоминаем эти параметры как альтернативный вид 0
            rdsAltConnAppearanceOp(Conn,RDS_CAOSET,0,&style);
          }
        // Устанавливаем альтернативный вид 0
        rdsAltConnAppearanceOp(Conn,RDS_CAOSETCURRENT,0,NULL);
      }
      //=========================================

      // Снять выделение связи
      void UnmarkConnection(RDS_CHANDLE Conn)
      { // Восстанавливаем исходный внешний вид связи
        rdsAltConnAppearanceOp(Conn,RDS_CAORESTORE,0,NULL);
      }
      //=========================================

      //=========================================
      // Имена и параметры функций блоков
      //=========================================

      // Функция сброса параметров в узле графа
      #define PROGGUIDEGRAPHPATHFUNC_RESET \
              "ProgrammersGuide.GraphPath.Reset"
      // Структура парметров функции
      typedef struct
      { DWORD servSize;  // Размер этой структуры
        BOOL ResetMark;  // TRUE – сбросить метку узла
        BOOL ResetBegin; // TRUE – сбросить флаг начала маршрута
        BOOL ResetEnd;   // TRUE – сбросить флаг конца маршрута
      } TProgGuideFuncResetParams;
      //=========================================

      // Функция получения параметров узла графа
      #define PROGGUIDEGRAPHPATHFUNC_GETPARAMS \
              "ProgrammersGuide.GraphPath.GetParams"
      // Среагировавший на функцию блок должен вернуть значение 1
      // Структура парметров функции
      typedef struct
      { DWORD servSize; // Размер этой структуры
        BOOL Marked;    // В узле есть метка
        double Mark;    // Значение метки
        BOOL Begin;     // Этот узел – начало маршрута
        BOOL End;       // Этот узел – конец маршрута
      } TProgGuideFuncGetParams;
      //=========================================

      // Функция поиска начала и конца маршрута
      // (вызывается у всех блоков подсистемы)
      #define PROGGUIDEGRAPHPATHFUNC_FIND \
              "ProgrammersGuide.GraphPath.Find"
      // Структура параметров функции
      typedef struct
      { DWORD servSize;         // Размер этой структуры
        RDS_BHANDLE BeginBlock; // Найденный идентификатор начала
        RDS_BHANDLE EndBlock;   // Найденный идентификатор конца
      } TProgGuideFuncFindParams;
      //=========================================

      // Пометить узел графа указанным вещественным числом и вызвать
      // эту же функцию у его соседей
      #define PROGGUIDEGRAPHPATHFUNC_MARK \
              "ProgrammersGuide.GraphPath.Mark"
      // Структура параметров функции
      typedef struct
      { DWORD servSize;      // Размер этой структуры
        double Mark;         // Устанавливаемое значение метки
        RDS_BHANDLE Previous;// Блок, от которого пришла метка
      } TProgGuideFuncMarkParams;
      //=========================================

      // Выделить маршрут от данного блока к началу
      #define PROGGUIDEGRAPHPATHFUNC_BACKTRACE \
              "ProgrammersGuide.GraphPath.BackTrace"
      // Параметров у функции нет
      //=========================================

      // Глобальные переменные для хранения идентификаторов функций блоков
      int GraphFuncFind=0,      // ProgrammersGuide.GraphPath.Find
          GraphFuncGetParams=0, // ProgrammersGuide.GraphPath.GetParams
          GraphFuncReset=0,     // ProgrammersGuide.GraphPath.Reset
          GraphFuncMark=0,      // ProgrammersGuide.GraphPath.Mark
          GraphFuncBackTrace=0; // ProgrammersGuide.GraphPath.BackTrace
      //=========================================

      //=========================================
      // Функции-оболочки (для лучшей читаемости)
      //=========================================

      // Сбросить у узлов графа в заданной подсистеме заданные маркеры
      void GraphPath_Reset(
        RDS_BHANDLE Sys, // Подсистема
        BOOL mark,       // Сбросить метки узлов
        BOOL begin,      // Сбросить начало маршрута
        BOOL end)        // Сбросить конец маршрута
      { // Структура параметров функции ProgrammersGuide.GraphPath.Reset
        TProgGuideFuncResetParams params;
        // Заполняем поле размера структуры парааметров
        params.servSize=sizeof(params);
        // Заполняем поля структуры параметров
        params.ResetMark=mark;
        params.ResetBegin=begin;
        params.ResetEnd=end;
        // Вызываем ProgrammersGuide.GraphPath.Reset у всех
        // блоков подсистемы Sys
        rdsBroadcastFunctionCallsEx(Sys,GraphFuncReset,&params,0);
      }
      //=========================================

      // Найти блоки начала и конца маршрута в заданной подсистеме
      BOOL GraphPath_GetTerminalBlocks(
        RDS_BHANDLE Sys,     // Подсистема с графом
        RDS_BHANDLE *pBegin, // Возвращаемый идентификатор начала
        RDS_BHANDLE *pEnd)   // Возвращаемый идентификатор конца
      { TProgGuideFuncFindParams params;
        // Заполняем поле размера структуры паараметров
        params.servSize=sizeof(params);
        // Обнуляем поля идентификаторов начала и конца
        params.BeginBlock=params.EndBlock=NULL;
        // Вызываем ProgrammersGuide.GraphPath.Find у всех блоков в
        // подсистеме Sys, разрешая блокам остановить вызовы
        rdsBroadcastFunctionCallsEx(Sys,GraphFuncFind,&params,
            RDS_BCALL_ALLOWSTOP);
        // Копируем найденные идентификаторы в переданные указатели
        if(pBegin) *pBegin=params.BeginBlock;
        if(pEnd) *pEnd=params.EndBlock;
        // Возвращаем TRUE, если установлены и начало, и конец
        return params.BeginBlock!=NULL && params.EndBlock!=NULL;
      }
      //=========================================

      // Поиск маршрута в графе в заданной подсистеме
      void GraphPath_FindPath(RDS_BHANDLE System)
      { RDS_BHANDLE StartBlock,EndBlock;
        TProgGuideFuncMarkParams markparams;

        // Считаем, что маркировка всего графа сброшена

        // Ищем начальную и конечную точку маршрута
        if(!GraphPath_GetTerminalBlocks(System,&StartBlock,&EndBlock))
          return; // Начало или конец не найдены
        // Начало маршрута – StartBlock, конец - EndBlock

        // Маркируем граф от начала маршрута
        markparams.servSize=sizeof(markparams);
        markparams.Mark=0.0; // Начало маркируется значением 0
        markparams.Previous=NULL;// Это значение не пришло от какого-то
                                 // соседнего блока
        // Вызываем функцию маркировки для начального блока
        rdsCallBlockFunction(StartBlock,GraphFuncMark,&markparams);

        // Теперь отслеживаем кратчайший путь в обратном направлении
        // (от конечного блока)
        rdsCallBlockFunction(EndBlock,GraphFuncBackTrace,NULL);
      }
      //=========================================

      //=========================================
      // Модель блока и дополнительные функции
      //=========================================

      // Макроопределения для статических переменных
      #define pStart    ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define sBegin (*((char *)(pStart+2*RDS_VSZ_S)))
      #define sEnd (*((char *)(pStart+2*RDS_VSZ_S+RDS_VSZ_L)))
      #define sInPath (*((char *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_L)))
      #define sMarked (*((char *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_L)))
      #define sPathMark (*((double *)(pStart+2*RDS_VSZ_S+4*RDS_VSZ_L)))
      //=========================================

      // Сделать этот блок началом маршрута (прототип функции)
      void GraphNode_SetBlockAsBegin(RDS_PBLOCKDATA BlockData);
      // Сделать этот блок концом маршрута (прототип функции)
      void GraphNode_SetBlockAsEnd(RDS_PBLOCKDATA BlockData);
      // Реакция блока на функцию ProgrammersGuide.GraphPath.Reset (прототип функции)
      void GraphNode_OnReset(RDS_PBLOCKDATA BlockData,
              TProgGuideFuncResetParams *reset);
      // Реакция блока на функцию ProgrammersGuide.GraphPath.Mark (прототип функции)
      void GraphNode_OnMarkBlock(RDS_PBLOCKDATA BlockData,
              TProgGuideFuncMarkParams *params);
      // Реакция блока на функцию ProgrammersGuide.GraphPath.BackTrace (прототип функции)
      void GraphNode_OnBackTracePath(RDS_PBLOCKDATA BlockData);
      // Программное рисование внешнего вида блока (прототип функции)
      void GraphNode_Draw(RDS_PBLOCKDATA BlockData,RDS_PDRAWDATA draw);
      //=========================================

      // Модель блока-узла графа
      extern "C" __declspec(dllexport)
        int RDSCALL GraphNode(int CallMode,
                RDS_PBLOCKDATA BlockData,
                LPVOID ExtParam)
      { // Вспомогательная - указатель на данные функции блока
        RDS_PFUNCTIONCALLDATA func;

        switch(CallMode)
          { // Инициализация
            case RDS_BFM_INIT:
              // Регистрация функций
              if(GraphFuncGetParams==0)
                GraphFuncGetParams=rdsRegisterFunction(
                  PROGGUIDEGRAPHPATHFUNC_GETPARAMS);
              if(GraphFuncReset==0)
                GraphFuncReset=rdsRegisterFunction(
                  PROGGUIDEGRAPHPATHFUNC_RESET);
              if(GraphFuncFind==0)
                GraphFuncFind=rdsRegisterFunction(
                  PROGGUIDEGRAPHPATHFUNC_FIND);
              if(GraphFuncMark==0)
                GraphFuncMark=rdsRegisterFunction(
                  PROGGUIDEGRAPHPATHFUNC_MARK);
              if(GraphFuncBackTrace==0)
                GraphFuncBackTrace=rdsRegisterFunction(
                  PROGGUIDEGRAPHPATHFUNC_BACKTRACE);
              break;

            // Проверка типов переменных
            case RDS_BFM_VARCHECK:
              return strncmp((char*)ExtParam,"{SSLLLLD",8)?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Вызов контекстного меню блока
            case RDS_BFM_CONTEXTPOPUP:
              // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
              //   в противном случае необходимо использовать версии функций
              //   с суффиксом "W" и символьные константы с префиксом "L"
              rdsAdditionalContextMenuItemEx("Начало маршрута",
                sBegin?RDS_MENU_DISABLED:0,0,0);
              rdsAdditionalContextMenuItemEx("Конец маршрута",
                sEnd?RDS_MENU_DISABLED:0,1,0);
              rdsAdditionalContextMenuItemEx("Сбросить все",0,2,0);
              break;

            // Быбор пункта меню
            case RDS_BFM_MENUFUNCTION:
              switch(((RDS_PMENUFUNCDATA)ExtParam)->Function)
                { case 0: // Начало маршрута
                    GraphNode_SetBlockAsBegin(BlockData);
                    rdsRefreshBlockWindows(BlockData->Parent,FALSE);
                    break;
                  case 1: // Конец маршрута
                    GraphNode_SetBlockAsEnd(BlockData);
                    rdsRefreshBlockWindows(BlockData->Parent,FALSE);
                    break;
                  case 2: // Сбросить все
                    GraphPath_Reset(BlockData->Parent,TRUE,TRUE,TRUE);
                    rdsRefreshBlockWindows(BlockData->Parent,FALSE);
                    break;
                }
              break;

            // Вызов функции блока
            case RDS_BFM_FUNCTIONCALL:
              func=(RDS_PFUNCTIONCALLDATA)ExtParam;
              if(func->Function==GraphFuncReset) // Сброс параметров
                GraphNode_OnReset(BlockData,
                  (TProgGuideFuncResetParams*)(func->Data));
              else if(func->Function==GraphFuncGetParams)
                { // Получение параметров узла
                  TProgGuideFuncGetParams *get=
                    (TProgGuideFuncGetParams*)(func->Data);
                  if(get!=NULL &&
                     get->servSize>=sizeof(TProgGuideFuncGetParams))
                    { // Допустимый размер структуры параметров
                      get->Marked=(sMarked!=0);
                      get->Mark=sPathMark;
                      get->Begin=(sBegin!=0);
                      get->End=(sEnd!=0);
                    }
                  return 1; // Блок является узлом графа
                }
              else if(func->Function==GraphFuncFind)
                { // Поиск начала и конца маршрута
                  TProgGuideFuncFindParams *find=
                    (TProgGuideFuncFindParams*)(func->Data);
                  if(find==NULL) break; // Нет параметров
                  if(find->servSize<sizeof(TProgGuideFuncFindParams))
                    break; // Недопустимый размер структуры параметров
                  if(sBegin) // Этот блок – начало маршрута
                    find->BeginBlock=BlockData->Block;
                  if(sEnd) // Этот блок – конец маршрута
                    find->EndBlock=BlockData->Block;
                  if(find->BeginBlock!=NULL && find->EndBlock!=NULL)
                    func->Stop=TRUE; // Оба конца маршрута найдены
                }
              else if(func->Function==GraphFuncMark) // Пометить граф
                GraphNode_OnMarkBlock(BlockData,
                  (TProgGuideFuncMarkParams*)(func->Data));
              else if(func->Function==GraphFuncBackTrace)
                GraphNode_OnBackTracePath(BlockData); // Выделить маршрут
              break;

            // Рисование
            case RDS_BFM_DRAW:
              GraphNode_Draw(BlockData,(RDS_PDRAWDATA)ExtParam);
              break;
          }
        return RDS_BFR_DONE;
      }
      //=========================================

      // Рисование узла графа
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      void GraphNode_Draw(RDS_PBLOCKDATA BlockData,RDS_PDRAWDATA draw)
      { static char beg[]="Н",end[]="К"; // Метки начала и конца
        int w,h;

        // Рисуем прямоугольник, цвет которого зависит от переменной
        // блока sInPath
        rdsXGSetPenStyle(0,PS_SOLID,1,0,R2_COPYPEN);
        rdsXGSetBrushStyle(0,RDS_GFS_SOLID,sInPath?0xff00:0xffffff);
        rdsXGRectangle(draw->Left,draw->Top,
            draw->Left+draw->Width,draw->Top+draw->Height);

        if(sBegin==0 && sEnd==0) // Нет флагов начала и конца маршрута
          return;
        // Устанавливаем шрифт высотой в весь блок
        rdsXGSetBrushStyle(0,RDS_GFS_EMPTY,0);
        rdsXGSetFont(0,"Arial Cyr",
          draw->Height,0,RUSSIAN_CHARSET,0,FALSE,FALSE,FALSE,FALSE);

        if(sBegin) // Рисуем метку начала
          { rdsXGGetTextSize(beg,&w,&h);
            rdsXGTextOut(draw->Left+(draw->Width-w)/2,
                         draw->Top+(draw->Height-h)/2,
                         beg);
          }
        if(sEnd) // Рисуем метку конца
          { rdsXGGetTextSize(end,&w,&h);
            rdsXGTextOut(draw->Left+(draw->Width-w)/2,
                         draw->Top+(draw->Height-h)/2,
                         end);
          }
      }
      //=========================================

      // Реакция блока на функцию ProgrammersGuide.GraphPath.Reset
      void GraphNode_OnReset(RDS_PBLOCKDATA BlockData,
              TProgGuideFuncResetParams *reset)
      { if(reset==NULL) return; // Нет параметров функции
        if(reset->servSize<sizeof(TProgGuideFuncResetParams))
          return; // Размер переданной структуры меньше ожидаемого

        if(reset->ResetMark)
          { // Сбрасываем выделение всех присоединенных к блоку связей
            RDS_CHANDLE c=NULL;
            for(;;) // Перебираем все связи, подключенные к блоку
              { c=rdsGetBlockLink(BlockData->Block,c,TRUE,TRUE,NULL);
                if(c==NULL) break; // Все связи перебраны
                // Снимаем выделение связи c
                UnmarkConnection(c);
              }
            // Сбрасываем флаги наличия метки и принадлежности
            // к выделенному маршруту
            sMarked=sInPath=0;
          }

        if(reset->ResetBegin) // Сбрасываем флаг начала маршрута
          sBegin=0;
        if(reset->ResetEnd) // Сбрасываем флаг конца маршрута
          sEnd=0;
      }
      //=========================================

      // Сделать данный блок началом маршрута
      void GraphNode_SetBlockAsBegin(RDS_PBLOCKDATA BlockData)
      { if(sBegin) // Блок уже является началом маршрута
          return;
        // Сбрасываем старый флаг начала маршрута и разметку всего графа
        GraphPath_Reset(BlockData->Parent,TRUE,TRUE,FALSE);
        // Устанавливаем флаг начала маршрута у данного блока
        sBegin=1;
        // Ищем маршрут в графе
        GraphPath_FindPath(BlockData->Parent);
      }
      //=========================================

      // Сделать данный блок концом маршрута
      void GraphNode_SetBlockAsEnd(RDS_PBLOCKDATA BlockData)
      { if(sEnd) // Блок уже является концом маршрута
          return;
        // Сбрасываем старый флаг конца маршрута и разметку всего графа
        GraphPath_Reset(BlockData->Parent,TRUE,FALSE,TRUE);
        // Устанавливаем флаг конца маршрута у данного блока
        sEnd=1;
        // Ищем маршрут в графе
        GraphPath_FindPath(BlockData->Parent);
      }
      //=========================================

      // Прототип функции обратного вызова для перечисления соседей блока
      BOOL RDSCALL GraphPath_MarkBlock_Callback(
          RDS_PPOINTDESCRIPTION src,
          RDS_PPOINTDESCRIPTION dest,
          LPVOID data);
      //=========================================

      // Пометить данный блок и его соседей
      void GraphNode_OnMarkBlock(RDS_PBLOCKDATA BlockData,
              TProgGuideFuncMarkParams *params)
      { if(params==NULL) return; // Нет параметров функции
        if(params->servSize<sizeof(TProgGuideFuncMarkParams))
          return; // Размер переданной структуры меньше ожидаемого

        if(sMarked!=0 && sPathMark<=params->Mark)
          return; // Блок уже помечен меньшим или таким же числом

        // Блок не помечен вообще или помечен большим числом -
        // помечаем его и его соседей новыми числами
        sMarked=1; // У блока есть метка
        sPathMark=params->Mark; // Новая метка блока

        if(sEnd) // Это – конец маршрута, дальше помечать незачем
          return;

        // Помечаем всех соседей блока, кроме params->Previous,
        // суммой метки этого блока и длины дуги к соседу
        rdsEnumConnectedBlocks(BlockData->Block,
          RDS_BEN_INPUTS|RDS_BEN_OUTPUTS,
          GraphPath_MarkBlock_Callback,params);
      }
      //=========================================

      // Функция обратного вызова для GraphNode_OnMarkBlock
      BOOL RDSCALL GraphPath_MarkBlock_Callback(
          RDS_PPOINTDESCRIPTION src,
          RDS_PPOINTDESCRIPTION dest,LPVOID data)
      { TProgGuideFuncMarkParams *src_params=
          (TProgGuideFuncMarkParams*)data;
        TProgGuideFuncMarkParams dest_params;
        double ArcLen;

        // Функция вызвана для пары блоков: src->Block – данный блок,
        // dest->Block – его сосед. Их соединяет связь dest->Owner (или
        // src->Owner – поля Owner у структур src и dest равны, поскольку
        // обе точки принадлежат одной и той же связи)

        // Сравниваем найденного соседа с блоком,
        // который не нужно помечать
        if(src_params->Previous==dest->Block)
          return TRUE;

        // Связь должна подходить ко входу блока dest->Block
        // (движение в графе возможно только по стрелкам)
        if(dest->Source) // Точка dest соединена с выходом блока
          return TRUE;

        // Является ли найденный сосед узлом графа?
        if(!rdsCallBlockFunction(dest->Block,GraphFuncGetParams,NULL))
          return TRUE; // Не является

        // Вычисляем длину дуги между блоками
        ArcLen=CalcArcLength(dest->Owner);
        if(ArcLen<0.0) // Связь разветвленная или оборванная
          return TRUE; // Такая связь не может быть дугой

        // Помечаем найденный соседний блок суммой маркировки данного
        //  блока (src_params->Mark) и длины дуги к найденному (ArcLen)
        dest_params.servSize=sizeof(dest_params);
        dest_params.Mark=src_params->Mark+ArcLen;
        dest_params.Previous=src->Block; // Блок, от которого
                                            // пришла метка
        rdsCallBlockFunction(dest->Block,GraphFuncMark,&dest_params);
        return TRUE;
      }
      //=========================================

      // Проследить и выделить маршрут от данного блока в
      // обратном направлении
      void GraphNode_OnBackTracePath(RDS_PBLOCKDATA BlockData)
      { RDS_BHANDLE PrevBlock;
        RDS_CHANDLE PrevConn,c;
        double minMark;
        RDS_CONNDESCRIPTION conndescr;
        RDS_POINTDESCRIPTION pointdescr;
        TProgGuideFuncGetParams params;

        if(!sMarked) // У блока нет метки
          return;

        sInPath=1; // Помечаем данный блок как принадлежащий маршруту

        if(sBegin) // Это начало маршрута – мы выделили его весь
          return;

        // Заполняем служебные поля размера у всех структур,
        // которые нам потребуются
        conndescr.servSize=sizeof(conndescr);
        pointdescr.servSize=sizeof(pointdescr);
        params.servSize=sizeof(params);

        // Ищем среди соседей данного блока блок с наименьшей меткой
        PrevBlock=NULL;
        // Перебираем все связи блока
        c=NULL;
        for(;;)
          { RDS_BHANDLE connBlock;
            double connMark;
            int numBlocks;
            // Получаем очередную связь блока, соединенную с его входом
            c=rdsGetBlockLink(BlockData->Block,c,TRUE,FALSE,NULL);
            if(c==NULL) break; // Связи кончились
            // Получаем параметры этой связи
            rdsGetConnDescription(c,&conndescr);
            // Перебираем все точки связи c и ищем среди них соединение
            // с узлом графа на другом ее конце
            connBlock=NULL;
            numBlocks=0;
            for(int i=0;i<conndescr.NumPoints;i++)
              { // Получаем описание точки связи i
                rdsGetPointDescription(c,i,&pointdescr);
                // Проверяем тип точки
                if(pointdescr.PointType==RDS_PTBUS)
                  { // Связь, соединенная с шиной, нам не годится
                    connBlock=NULL;
                    break;
                  }
                if(pointdescr.PointType!=RDS_PTBLOCK)
                  continue;
                // Найдена точка соединения с блоком
                numBlocks++;
                if(numBlocks>2) // Разветвленная связь – не годится
                  { connBlock=NULL;
                    break;
                  }
                if(pointdescr.Block==BlockData->Block)
                  continue; // Это не сосед, а сам данный блок
                // Получаем параметры узла блока-соседа
                if(!rdsCallBlockFunction(pointdescr.Block,
                     GraphFuncGetParams,&params))
                  { // Блок не является узлом графа
                    connBlock=NULL;
                    break;
                  }
                // Нашли соседний блок, являющийся узлом графа
                if(params.Marked) // У соседнего узла есть метка
                  { connBlock=pointdescr.Block; // Запоминаем
                    connMark=params.Mark;
                  }
              } // for(int i=0;...)
            // Цикл перебора точек связи закончен. В cоnnBlock должен
            // находиться идентификатор узла, который эта связь соединяет
            // с данным блоком, а в connMark – его метка.
            if(connBlock) // Есть узел графа на другом конце связи
              { if(PrevBlock==NULL || minMark>connMark)
                  { // Найден узел с меньшей маркировкой
                    PrevBlock=connBlock;
                    minMark=connMark;
                    PrevConn=c;
                  }
              }
          } // for(;;)

        // Перебор связей данного блока закончен. В PrevBlock должен
        // находиться идентификатор соседа с наименьшей меткой, из
        // которого можно попасть в данный блок, а в PrevConn -
        // идентификатор соединяющей их связи

       if(PrevBlock)
         { // Визуально выделяем связь
           MarkConnection(PrevConn);
           // Вызываем функцию обратного прослеживания маршрута
           // от найденного блока
           rdsCallBlockFunction(PrevBlock,GraphFuncBackTrace,NULL);
         }
      }
      //=========================================

      // Отмена макроопределений переменных блока
      #undef sPathMark
      #undef sMarked
      #undef sInPath
      #undef sEnd
      #undef sBegin
      #undef Ready
      #undef Start
      #undef pStart
      //=========================================