Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока-узла графа, способного находить и показывать кратчайший путь между двумя заданными узлами. В примере из §2.13.4 прямые вызовы функций блоков заменены на отложенные для исключения переполнения стека в сложных графах. Изменения выделены цветом.
// Использование функций блоков для поиска пути в графе // (вариант с отложенным вызовом функций) #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,¶ms,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,¶ms, 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); rdsQueueCallBlockFunction(StartBlock,GraphFuncMark, &markparams,sizeof(markparams),RDS_BCALL_FIRST); // Теперь отслеживаем кратчайший путь в обратном направлении // (от конечного блока) // rdsCallBlockFunction(EndBlock,GraphFuncBackTrace,NULL); rdsQueueCallBlockFunction(EndBlock,GraphFuncBackTrace, NULL,0,RDS_BCALL_LAST); } //========================================= //========================================= // Модель блока и дополнительные функции //========================================= // Макроопределения для статических переменных #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); rdsQueueCallBlockFunction(dest->Block,GraphFuncMark, &dest_params,sizeof(dest_params),RDS_BCALL_FIRST); 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,¶ms)) { // Блок не является узлом графа 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); rdsQueueCallBlockFunction(PrevBlock,GraphFuncBackTrace, NULL,0,RDS_BCALL_LAST); } } //========================================= // Отмена макроопределений переменных блока #undef sPathMark #undef sMarked #undef sInPath #undef sEnd #undef sBegin #undef Ready #undef Start #undef pStart //=========================================