Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока-генератора синусоиды, косинусоиды или прямоугольных импульсов, выводящего всплывающую подсказку со значениями своих параметров. Изменения относительно примера из §2.8.3 выделены цветом.
// Генератор с всплывающей подсказкой #include <windows.h> #include <math.h> #include <stdio.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; } //========= Конец главной функции ========= //========================================= //= Генератор = //========================================= //====== Класс личной области данных ====== class TTestGenData { public: int Type; // Тип (0-sin,1-cos,2-прямоугольные) double Period; // Период double Impulse; // Длительность импульса RDS_PDYNVARLINK Time; // Связь с динамической // переменной времени int Setup(void); // Функция настройки void SaveText(void); // Сохранение параметров void LoadText(char *text);// Загрузка параметров void PopupHint(void); // Всплывающая подсказка TTestGenData(void) // Конструктор класса { Type=0; Period=1.0; Impulse=0.5; // Подписка на динамическую переменную времени Time=rdsSubscribeToDynamicVar(RDS_DVPARENT, "DynTime", "D", TRUE); }; ~TTestGenData(void) // Деструктор класса { // Прекращение подписки rdsUnsubscribeFromDynamicVar(Time); }; }; //==== Прототип функции обратного вызова окна настроек ==== void RDSCALL TestGenDataCheckFunc2(RDS_HOBJECT win, RDS_PFORMSERVFUNCDATA data); //====== Функция редактирования параметров ====== // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" int TTestGenData::Setup(void) { RDS_HOBJECT window; // Идентификатор вспомогательного объекта BOOL ok; // Пользователь нажал "OK" // Создание окна window=rdsFORMCreate(FALSE,-1,-1,"Простой генератор"); // Добавление полей ввода rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_COMBOLIST, "Вид:",210); rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_EDIT, "Период:",80); rdsFORMAddEdit(window,0,3,RDS_FORMCTRL_EDIT, "Длительность:",80); // Установка списка вариантов rdsSetObjectStr(window,1,RDS_FORMVAL_LIST, "Синус\nКосинус\nПрямоугольные импульсы"); // Занесение исходных значений в поля ввода rdsSetObjectInt(window,1,RDS_FORMVAL_VALUE,Type); rdsSetObjectDouble(window,2,RDS_FORMVAL_VALUE,Period); rdsSetObjectDouble(window,3,RDS_FORMVAL_VALUE,Impulse); // Включение дополнительной панели слева от полей ввода rdsFORMEnableSidePanel(window,1,-1); // Добавление области для рисования графика rdsFORMAddEdit(window,1,4,RDS_FORMCTRL_PAINTBOX,NULL,100); // Автоматическое вычисление высоты этой области rdsSetObjectInt(window,4,RDS_FORMVAL_PBHEIGHT,-1); // Открытие окна с другой функцией обратного вызова ok=rdsFORMShowModalServ(window,TestGenDataCheckFunc2); if(ok) { // Нажата кнопка OK - запись параметров обратно в блок Type=rdsGetObjectInt(window,1,RDS_FORMVAL_VALUE); Period=rdsGetObjectDouble(window,2,RDS_FORMVAL_VALUE); Impulse=rdsGetObjectDouble(window,3,RDS_FORMVAL_VALUE); } // Уничтожение окна rdsDeleteObject(window); // Возвращаемое значение return ok?RDS_BFR_MODIFIED:RDS_BFR_DONE; } //====== Функция обратного вызова для окна настроек ====== void RDSCALL TestGenDataCheckFunc2(RDS_HOBJECT win, RDS_PFORMSERVFUNCDATA data) { // Считать номер пункта выпадающего списка int type=rdsGetObjectInt(win,1,RDS_FORMVAL_VALUE); // Вспомогательные переменные для рисования графика int y0,y_ampl,x0,x1; double pix_period; switch(data->Event) { // Изменение поля ввода case RDS_FORMSERVEVENT_CHANGE: // Запретить ввод длительности импульса для sin и cos rdsSetObjectInt(win,3,RDS_FORMVAL_ENABLED,type==2); // Перерисовать график rdsCommandObject(win,RDS_FORM_INVALIDATE); break; // Рисование case RDS_FORMSERVEVENT_DRAW: // Заливка фона белым цветом rdsXGSetBrushStyle(0,RDS_GFS_SOLID,0xffffff); rdsXGFillRect(data->Left, data->Top, data->Left+data->Width, data->Top+data->Height); // Координаты рисования x0=data->Left+10; // Начало графика x1=data->Left+data->Width-10;// Конец графика y0=data->Top+data->Height/2; // Центр по вертикали y_ampl=(data->Height-20)/2; // Амплитуда pix_period=0.5*(x1-x0); // Период на рисунке // Координатные оси rdsXGSetPenStyle(0,PS_SOLID,1,0,R2_COPYPEN); rdsXGMoveTo(data->Left+5,y0); rdsXGLineTo(data->Left+data->Width-5,y0); rdsXGMoveTo(x0,data->Top+5); rdsXGLineTo(x0,data->Top+data->Height-5); // График rdsXGSetPenStyle(RDS_GFWIDTH,0,3,0,0); if(type==2) // Прямоугольные импульсы { double period,impulse,pix_impulse; // Чтение введенных пользователем значений period=rdsGetObjectDouble(win,2,RDS_FORMVAL_VALUE); impulse=rdsGetObjectDouble(win,3,RDS_FORMVAL_VALUE); if(period==0.0) // Нельзя вычислить частоту return; // Длительность импульса на рисунке pix_impulse=impulse*pix_period/period; // Первый период rdsXGMoveTo(x0,y0+y_ampl); rdsXGLineTo(x0,y0-y_ampl); rdsXGLineTo(x0+pix_impulse,y0-y_ampl); rdsXGLineTo(x0+pix_impulse,y0+y_ampl); rdsXGLineTo(x0+pix_period,y0+y_ampl); // Второй период rdsXGLineTo(x0+pix_period,y0-y_ampl); rdsXGLineTo(x0+pix_period+pix_impulse,y0-y_ampl); rdsXGLineTo(x0+pix_period+pix_impulse,y0+y_ampl); rdsXGLineTo(x1,y0+y_ampl); } else // Синус или косинус { double t,y; // Цикл по горизонтали с шагом в 3 точки for(int x=x0;x<=x1;x+=3) { t=2*M_PI*(x-x0)/pix_period; y=y_ampl*((type==0)?sin(t):cos(t)); if(x==x0) // Первая точка – установить позицию rdsXGMoveTo(x,y0-y); else // Рисовать линию от предыдущей точки rdsXGLineTo(x,y0-y); } // for(int x=x0...) } break; } // switch } //========================================= //============= Модель блока ============== extern "C" __declspec(dllexport) int RDSCALL TestGen(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define y (*((double *)(pStart+2*RDS_VSZ_S))) // Вспомогательная переменная – указатель на личную область // данных блока, приведенный к правильному типу TTestGenData *data; switch(CallMode) { // Инициализация case RDS_BFM_INIT: BlockData->BlockData=new TTestGenData(); break; // Очистка case RDS_BFM_CLEANUP: data=(TTestGenData*)(BlockData->BlockData); delete data; break;// Всплывающая подсказка case RDS_BFM_POPUPHINT: data=(TTestGenData*)(BlockData->BlockData); data->PopupHint(); break;// Проверка типа переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Запись параметров в текстовом формате case RDS_BFM_SAVETXT: data=(TTestGenData*)(BlockData->BlockData); data->SaveText(); break; // Загрузка параметров в текстовом формате case RDS_BFM_LOADTXT: data=(TTestGenData*)(BlockData->BlockData); data->LoadText((char*)ExtParam); break; // Функция настройки case RDS_BFM_SETUP: data=(TTestGenData*)(BlockData->BlockData); return data->Setup(); // Изменение динамической переменной или запуск расчета case RDS_BFM_STARTCALC: case RDS_BFM_DYNVARCHANGE: data=(TTestGenData*)(BlockData->BlockData); if(data->Period==0.0) // Нельзя вычислить частоту return 0; // Проверка наличия переменной “DynTime" if(data->Time!=NULL && data->Time->Data!=NULL) { // Динамическая переменная найдена – чтение значения double t=*((double*)data->Time->Data); switch(data->Type) { case 0: // Синус y=sin(2*M_PI*t/data->Period); break; case 1: // Косинус y=cos(2*M_PI*t/data->Period); break; case 2: // Прямоугольные импульсы t=fmod(t,data->Period); y=(t>data->Impulse)?-1.0:1.0; break; } // Взвести Ready для передачи выхода по связям Ready=1; } break; } return RDS_BFR_DONE; // Отмена макроопределений #undef y #undef Ready #undef Start #undef pStart } //========================================= // Глобальный массив ключевых слов // Индексы 0 1 2 const char *TestGen_Keywords[]={"type","period","impulse",NULL}; // define-константы для индексов #define TESTGEN_KW_TYPE 0 #define TESTGEN_KW_PERIOD 1 #define TESTGEN_KW_IMPULSE 2 //========================================= // Функция сохранения параметров void TTestGenData::SaveText(void) { // Запись "type" и целого значения rdsWriteWordValueText(TestGen_Keywords[TESTGEN_KW_TYPE],Type); // Запись "period" и вещественного значения rdsWriteWordDoubleText(TestGen_Keywords[TESTGEN_KW_PERIOD], Period); // Запись "impulse" и вещественного значения if(Type==2) rdsWriteWordDoubleText(TestGen_Keywords[TESTGEN_KW_IMPULSE], Impulse); } //========================================= // Функция загрузки параметров void TTestGenData::LoadText(char *text) { RDS_HOBJECT Parser; // Вспомогательный объект BOOL work=TRUE; // Флаг цикла // Создание объекта для разбора текста Parser=rdsSTRCreateTextReader(TRUE); // Передача объекту массива ключевых слов rdsSTRAddKeywordsArray(Parser,TestGen_Keywords,-1,0); // Передача объекту разбираемого текста rdsSetObjectStr(Parser,RDS_HSTR_SETTEXT,0,text); // Цикл до тех пор, пока в тексте не кончатся слова while(work) { int id; // Уникальный идентификатор слова // Извлечь из текста и опознать слово id=rdsSTRGetWord(Parser,NULL,NULL,NULL,TRUE); // Действия в зависимости от слова switch(id) { // Нет слова – конец текста case RDS_HSTR_DEFENDOFTEXT: work=FALSE; // Выйти из цикла break; // Перевод строки – пропускаем case RDS_HSTR_DEFENDOFLINE: break; // Слово "type" case TESTGEN_KW_TYPE: // Извлекаем следующее слово и переводим в целое Type=rdsGetObjectInt(Parser,RDS_HSTR_READINT,TRUE); break; // Слово "period" case TESTGEN_KW_PERIOD: // Извлекаем следующее слово и переводим в double Period=rdsGetObjectDouble(Parser,RDS_HSTR_READDOUBLE,TRUE); break; // Слово "impulse" case TESTGEN_KW_IMPULSE: // Извлекаем следующее слово и переводим в double Impulse=rdsGetObjectDouble(Parser,RDS_HSTR_READDOUBLE,TRUE); break; default: // Слово не опознано – ошибка work=FALSE; // Выйти из цикла } // Конец switch(...) } // Конец while(work) // Удаление вспомогательного объекта rdsDeleteObject(Parser); } //=========================================// Вывод всплывающей подсказки // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TTestGenData::PopupHint(void) { // Есть ли доступ к переменной времени? if(Time==NULL || Time->Data==NULL) // Доступа нет rdsSetHintText("ОШИБКА: в схеме нет переменной DynTime"); else // Доступ есть { char *str=NULL, *period=rdsDtoA(Period,-1,NULL), *impulse=rdsDtoA(Impulse,-1,NULL); switch(Type) { case 0: // Синус str=rdsDynStrCat("Генератор - синус\nПериод: ", period,FALSE); break; case 1: // Косинус str=rdsDynStrCat("Генератор – косинус\nПериод: ", period,FALSE); break; case 2: // Импульсы str=rdsDynStrCat("Генератор – прямоугольные импульсы\n" "Период: ",period,FALSE); rdsAddToDynStr(&str,"\nДлительность импульса: ",FALSE); rdsAddToDynStr(&str,impulse,FALSE); break; } // Установка текста подсказки rdsSetHintText(str); // Освобождение памяти, занятой динамическими строками rdsFree(str); rdsFree(period); rdsFree(impulse); } } //=================================================Полный исходный текст на языке C++ для библиотеки (DLL) с моделью блока-графика, показывающего всплывающую подсказку с координатами точки графика под курсором мыши. Изменения относительно модели из §2.10.3 выделены цветом.
// График с всплывающей подсказкой #include <windows.h> #include <stdio.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; } //========= Конец главной функции ========= //========================================= // Простой график – личная область данных //========================================= class TSimplePlotData { private: // Запомненные координаты поля графика int Gr_x1,Gr_x2,Gr_y1,Gr_y2; // Масштаб окна на момент последнего рисования double OldZoom; // Индекс последнего нарисованного отсчета int LastDrawnIndex; // Настроечные параметры графика (цвета, шаг и т.п.) double TimeStep; // Шаг записи отсчетов RDS_SERVFONTPARAMS Font; // Шрифт чисел на осях COLORREF BorderColor; // Цвет рамки вокруг блока COLORREF FillColor; // Цвет фона блока COLORREF PlotBorderColor;// Цвет рамки поля графика COLORREF PlotFillColor; // Цвет фона поля графика COLORREF LineColor; // Цвет лини графика int LineWidth; // Толщина линии графика // Ось X double Xmin,Xmax; // Диапазон double XGridStep; // Шаг чисел на осях int XNumDecimal; // Дробная часть чисел на осях // Ось Y double Ymin,Ymax; // Диапазон double YGridStep; // Шаг чисел на осях int YNumDecimal; // Дробная часть чисел на осях // Массивы для хранения отсчетов графика double *Times; // Массив отсчетов времени double *Values; // Массив значений int Count; // Размер массивов int NextIndex; // Индекс для записи следующего значения double NextTime; // Время записи следующего значения RDS_PDYNVARLINK Time; // Связь с динамической переменной // времени ("DynTime") public: // Функция сброса запомненного масштаба последнего рисования void ResetCoords(void){OldZoom=-1.0;}; // Функция отведения массивов отсчетов void AllocateArrays(void); // Функция освобождения массивов отсчетов void ClearArrays(void); // Добавление очередной точки в массив отсчетов графика void AddPoint(double v); int Setup(void); // Функция настройки параметров void SaveText(void); // Функция сохранения параметров void LoadText(char *text); // Функция загрузки параметров void Draw(RDS_PDRAWDATA DrawData); // Функция рисования void DrawFast(RDS_PDRAWDATA DrawData); // Функция быстрого рисования // Рисование иконки при отсутствии доступа к DynTime void DrawAdditional(RDS_PDRAWDATA DrawData);// Поиск индекса отсчета, соответствующего времени t int FindTimeIndex(double t); // Вывод всплывающей полсказки void PopupHint(RDS_PPOPUPHINTDATA hintdata);TSimplePlotData(void); // Конструктор класса ~TSimplePlotData(); // Деструктор класса }; //========================================= // Модель блока extern "C" __declspec(dllexport) int RDSCALL SimplePlot( int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define x (*((double *)(pStart+2*RDS_VSZ_S))) // Указатель на личную область, приведенный к правильному типу TSimplePlotData *data=(TSimplePlotData*)(BlockData->BlockData); switch(CallMode) { // Инициализация блока case RDS_BFM_INIT: BlockData->BlockData=new TSimplePlotData(); break; // Очистка данных блока case RDS_BFM_CLEANUP: delete data; break; // Проверка типов статических переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG;// Всплывающая подсказка case RDS_BFM_POPUPHINT: data->PopupHint((RDS_PPOPUPHINTDATA)ExtParam); break;// Функция настройки параметров case RDS_BFM_SETUP: data->ResetCoords(); // Сброс запомненных значений return data->Setup(); // Загрузка параметров в текстовом формате case RDS_BFM_LOADTXT: data->LoadText((char*)ExtParam); data->ResetCoords(); // Сброс запомненных значений break; // Созранение параметров в текстовом формате case RDS_BFM_SAVETXT: data->SaveText(); break; // Изменение размеров блока case RDS_BFM_RESIZE: data->ResetCoords(); // Сброс запомненных значений break; // Рисование внешнего вида блока case RDS_BFM_DRAW: //data->Draw((RDS_PDRAWDATA)ExtParam); data->DrawFast((RDS_PDRAWDATA)ExtParam); break; // Дополнительное рисование case RDS_BFM_DRAWADDITIONAL: data->DrawAdditional((RDS_PDRAWDATA)ExtParam); break; // Запуск расчета case RDS_BFM_STARTCALC: if(((RDS_PSTARTSTOPDATA)ExtParam)->FirstStart) data->AllocateArrays(); // Первый запуск break; // Сброс расчета case RDS_BFM_RESETCALC: data->ClearArrays(); break; // Реакция на изменение динамической переменной case RDS_BFM_DYNVARCHANGE: data->AddPoint(x); break; } return RDS_BFR_DONE; // Отмена макроопределений для переменных #undef x #undef Ready #undef Start #undef pStart } //========================================= // Конструктор класса личной области данных графика TSimplePlotData::TSimplePlotData(void) { // Инициализация OldZoom: значение -1 приведет к принудительному // вычислению Gr_x1,Gr_x2,Gr_y1 и Gr_y2 в ближайшем вызове // функции Draw OldZoom=-1.0; // Инициализация LastDrawnIndex LastDrawnIndex=-1; // Присвоение начальных значений параметрам TimeStep=0.1; // Шаг записи BorderColor=0; // Цвет рамки вокруг блока FillColor=0xffffff; // Цвет фона блока PlotBorderColor=0; // Цвет рамки окна графика и сетки PlotFillColor=0xffffff; // Цвет окна графика LineColor=0; // Цвет линии графика LineWidth=1; // Толщина линии графика // Параметры шрифта Font.servSize=sizeof(Font); strcpy(Font.Name,"Arial"); Font.SizePriority=FALSE; Font.Height=15; Font.Color=0; Font.Bold=Font.Italic=Font.Underline=Font.StrikeOut=FALSE; Font.CharSet=DEFAULT_CHARSET; // Диапазоны осей, шаг сетки, число десятичных знаков // в числах на осях Xmin=0.0; Xmax=10.0; XGridStep=5.0; XNumDecimal=0; Ymin=-1.0; Ymax=1.0; YGridStep=0.5; YNumDecimal=1; // Обнуление указателей на массивы и их размера // (массивы еще не отведены) Times=Values=NULL; Count=NextIndex=0; NextTime=Xmin; // Подписка на динамическую переменную времени Time=rdsSubscribeToDynamicVar( RDS_DVPARENT, // В родительской подсистеме "DynTime", // Имя переменной "D", // Тип переменной (double) TRUE); // Искать по иерархии } //========================================= // Деструктор класса TSimplePlotData::~TSimplePlotData() { rdsUnsubscribeFromDynamicVar(Time); // Прекратить подписку ClearArrays(); // Освободить массивы } //========================================= // Рисование внешнего вида блока void TSimplePlotData::Draw(RDS_PDRAWDATA DrawData) { // Вспомогательные переменные // int Gr_x1,Gr_x2,Gr_y1,Gr_y2; - эти переменные стали полями класса int x1,y1,x2,y2,textheight,w1,w2; char buf[80]; // Рамка графика rdsXGSetPenStyle(0,PS_SOLID,1,BorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_SOLID,FillColor); rdsXGRectangle(DrawData->Left,DrawData->Top, DrawData->Left+DrawData->Width, DrawData->Top+DrawData->Height); // Необходимо вычислить координаты поля графика относительно // верхнего левого угла блока // Установка параметров шрифта с учетом масштаба rdsXGSetFontByParStr(&Font,DrawData->DoubleZoom); rdsXGGetTextSize("0",NULL,&textheight); if(DrawData->DoubleZoom!=OldZoom) // Масштаб изменен { // Зазор сверху – половина высоты цифры + 1 точка Gr_y1=textheight/2+1; // Зазор снизу – полная высота цифры + 1 точка Gr_y2=DrawData->Height-textheight-1; // Зазор слева – ширина самого длинного числа вертикальной // оси или половина ширины Xmin sprintf(buf," %.*lf ",YNumDecimal,Ymin); rdsXGGetTextSize(buf,&w1,NULL); // Ширина Ymin sprintf(buf," %.*lf ",YNumDecimal,Ymax); rdsXGGetTextSize(buf,&w2,NULL); // Ширина Ymax if(w2>w1) w1=w2; sprintf(buf," %.*lf ",XNumDecimal,Xmin); rdsXGGetTextSize(buf,&w2,NULL); // Ширина Xmin w2/=2; if(w2>w1) w1=w2; Gr_x1=w1; // Зазор справа – половина ширины Xmax sprintf(buf," %.*lf ",XNumDecimal,Xmax); rdsXGGetTextSize(buf,&w2,NULL); // Ширина Xmax w2/=2; Gr_x2=DrawData->Width-w2; // Запоминание нового масштаба OldZoom=DrawData->DoubleZoom; } // if(DrawData->DoubleZoom!=OldZoom) // Абсолютные (на рабочем поле) координаты поля графика x1=DrawData->Left+Gr_x1; x2=DrawData->Left+Gr_x2; y1=DrawData->Top+Gr_y1; y2=DrawData->Top+Gr_y2; if(x1>=x2 || y1>=y2) // Негде рисовать return; // Прямоугольник поля графика rdsXGSetPenStyle(0,PS_SOLID,1,PlotBorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_SOLID,PlotFillColor); rdsXGRectangle(x1,y1,x2,y2); // Установка пунктирного стиля линии rdsXGSetPenStyle(0,PS_DOT,1,PlotBorderColor,R2_COPYPEN); rdsXGSetBrushStyle(0,RDS_GFS_EMPTY,0); // Без заливки // Горизонтальная ось с сеткой for(double x=Xmin;x<=Xmax+XGridStep*0.5;x+=XGridStep) { // ix - координата линии на рабочем поле int ix=x1+(x-Xmin)*(x2-x1)/(Xmax-Xmin); if(ix>x1 && ix<x2) // Чертим вертикальную линию { rdsXGMoveTo(ix,y1); rdsXGLineTo(ix,y2); } // Вывод числа на оси под полем sprintf(buf,"%.*lf",XNumDecimal,x); rdsXGGetTextSize(buf,&w1,NULL); rdsXGTextOut(ix-w1/2,y2,buf); } // Вертикальная ось с сеткой for(double y=Ymin;y<=Ymax+YGridStep*0.5;y+=YGridStep) { // iy - координата линии на рабочем поле int iy=y2-(y-Ymin)*(y2-y1)/(Ymax-Ymin); if(iy>y1 && iy<y2) // Чертим горизонтальную линию { rdsXGMoveTo(x1,iy); rdsXGLineTo(x2,iy); } // Вывод числа на оси слева от поля sprintf(buf,"%.*lf ",YNumDecimal,y); rdsXGGetTextSize(buf,&w1,&textheight); rdsXGTextOut(x1-w1-2,iy-textheight/2,buf); } // Если массивы не пустые – рисовать график if(Count) { RECT r; // Установить область отсечения рисования по полю графика r.left=x1+1; r.top=y1+1; r.right=x2-1; r.bottom=y2-1; rdsXGSetClipRect(&r); // Установить сплошной стиль линии, заданный для // графика цвет и толщину линии с учетом масштаба rdsXGSetPenStyle(0,PS_SOLID, LineWidth*DrawData->DoubleZoom, LineColor,R2_COPYPEN); // Строим ломанную линию по отсчетам из массивов for(int i=0;i<NextIndex;i++) { // Преобразуем вещественные отсчеты в целочисленные // координаты на рабочем поле int ix=x1+(Times[i]-Xmin)*(x2-x1)/(Xmax-Xmin), iy=y2-(Values[i]-Ymin)*(y2-y1)/(Ymax-Ymin); if(i) // Не первая точка – строим линию от предыдущей rdsXGLineTo(ix,iy); else // Первая точка графика – делаем ее текущей rdsXGMoveTo(ix,iy); } // Запоминаем последний нарисованный отсчет LastDrawnIndex=NextIndex-1; // Отмена отсечения rdsXGSetClipRect(NULL); } } //========================================= // Рисование изменений void TSimplePlotData::DrawFast(RDS_PDRAWDATA DrawData) { int x1,y1,x2,y2; // Если RDS требует полного рисования, или полное рисование // еще не проводилось, вызывается старая функция Draw if(DrawData->FullDraw || DrawData->DoubleZoom!=OldZoom) { Draw(DrawData); return; } // Вычисление абсолютных координат поля графика x1=DrawData->Left+Gr_x1; x2=DrawData->Left+Gr_x2; y1=DrawData->Top+Gr_y1; y2=DrawData->Top+Gr_y2; if(x1>=x2 || y1>=y2) // Негде рисовать return; // Если массивы не пустые – рисовать график, начиная // с последней уже нарисованной точки if(Count) { RECT r; // Установить область отсечения рисования по полю графика r.left=x1+1; r.top=y1+1; r.right=x2-1; r.bottom=y2-1; rdsXGSetClipRect(&r); // Установить сплошной стиль линии, заданный для // графика цвет и толщину линии с учетом масштаба rdsXGSetPenStyle(0,PS_SOLID, LineWidth*DrawData->DoubleZoom, LineColor,R2_COPYPEN); // Проверка допустимости LastDrawnIndex – на всякий случай if(LastDrawnIndex<0) LastDrawnIndex=0; // Строим ломанную линию по отсчетам из массивов, // начиная с LastDrawIndex for(int i=LastDrawnIndex;i<NextIndex;i++) { int ix=x1+(Times[i]-Xmin)*(x2-x1)/(Xmax-Xmin), iy=y2-(Values[i]-Ymin)*(y2-y1)/(Ymax-Ymin); if(i!=LastDrawnIndex) rdsXGLineTo(ix,iy); else rdsXGMoveTo(ix,iy); } // Запоминаем последний нарисованный отсчет LastDrawnIndex=NextIndex-1; // Отмена отсечения rdsXGSetClipRect(NULL); } } //========================================= // Дополнительное рисование void TSimplePlotData::DrawAdditional(RDS_PDRAWDATA DrawData) { // Проверка доступа к переменной времени if(Time==NULL || Time->Data==NULL) // Доступа нет { int w,h; // Константа, указывающая на стандартную иконку RDS DWORD icon=RDS_STDICON_YELCIRCEXCLAM; // Определяем размер иконки и выводим ее в центре блока if(rdsXGGetStdIconSize(icon,&w,&h)) rdsXGDrawStdIcon(DrawData->Left+(DrawData->Width-w)/2, DrawData->Top+(DrawData->Height-h)/2, icon); } } //========================================= // Отведение памяти под массивы void TSimplePlotData::AllocateArrays(void) { // Сначала нужно очистить массивы, если они были отведены ранее ClearArrays(); // При нулевом или отрицательном шаге записи отсчетов // работа блока невозможна if(TimeStep<=0.0) return; // Вычисление требуемого числа отсчетов по диапазону оси // времени и шагу записи Count=(Xmax-Xmin)/TimeStep+1; // Число отсчетов должно быть положительным if(Count<=0) {Count=0; return; } // Отведение памяти – по Count чисел double Times=new double[Count]; Values=new double[Count]; // Первый свободный индкс массива - 0 NextIndex=0; // Момент записи отсчета – начало диапазона оси времени NextTime=Xmin; } //========================================= // Освобождение массивов void TSimplePlotData::ClearArrays(void) { if(Count) // Массивы были отведены { delete[] Times; delete[] Values; } // Обнуление указателей и Count Times=Values=NULL; Count=NextIndex=0; } //========================================= // Добавление отсчета в массив void TSimplePlotData::AddPoint(double v) { double t; if(NextIndex>=Count) // Весь массив заполнен return; if(Time==NULL || Time->Data==NULL) // Нет доступа к “DynTime" return; // Получение значения времени из “DynTime" t=*((double*)Time->Data); if(t<NextTime) // Еще не пришло время писать отсчет return; // Достигнуто время записи Values[NextIndex]=v; Times[NextIndex]=t; NextIndex++; // Следующий отсчет – в следующий индекс NextTime+=TimeStep; // Время записи следующего отсчета } //========================================= // Сохранение параметров в текстовом виде void TSimplePlotData::SaveText(void) { RDS_HOBJECT ini; char *str; // Создание объекта для работы с текстом ini=rdsINICreateTextHolder(TRUE); // Создание в тексте секции "[General]" rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"General"); // Запись в секцию различных параметров rdsINIWriteDouble(ini,"TimeStep",TimeStep); rdsINIWriteDouble(ini,"Xmin",Xmin); rdsINIWriteDouble(ini,"Xmax",Xmax); rdsINIWriteDouble(ini,"XGridStep",XGridStep); rdsINIWriteInt(ini,"XNumDecimal",XNumDecimal); rdsINIWriteDouble(ini,"Ymin",Ymin); rdsINIWriteDouble(ini,"Ymax",Ymax); rdsINIWriteDouble(ini,"YGridStep",YGridStep); rdsINIWriteInt(ini,"YNumDecimal",YNumDecimal); // Создание в тексте секции "[Visuals]" rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"Visuals"); // Запись в секцию различных параметров rdsINIWriteInt(ini,"BorderColor",(int)BorderColor); rdsINIWriteInt(ini,"FillColor",(int)FillColor); rdsINIWriteInt(ini,"PlotBorderColor",(int)PlotBorderColor); rdsINIWriteInt(ini,"PlotFillColor",(int)PlotFillColor); rdsINIWriteInt(ini,"LineColor",(int)LineColor); rdsINIWriteInt(ini,"LineWidth",LineWidth); // Преобразование описания шрифта в строку для сохранения str=rdsStructToFontText(&Font,NULL); // Запись строки с описанием шрифта rdsINIWriteString(ini,"Font",str); rdsFree(str); // Освобождение памяти, занятой строкой // Запись сформированного текста в файл схемы или буфер обмена rdsCommandObject(ini,RDS_HINI_SAVEBLOCKTEXT); // Уничтожение вспомогательного объекта rdsDeleteObject(ini); } //========================================= // Загрузка параметров в текстовом виде из строки text void TSimplePlotData::LoadText(char *text) { RDS_HOBJECT ini; char *str; // Создание объекта для работы с текстом ini=rdsINICreateTextHolder(TRUE); // Загрузка текста в объект rdsSetObjectStr(ini,RDS_HINI_SETTEXT,0,text); // Если в тексте есть секция "General", загрузить из нее данные if(rdsINIOpenSection(ini,"General")) { TimeStep=rdsINIReadDouble(ini,"TimeStep",TimeStep); Xmin=rdsINIReadDouble(ini,"Xmin",Xmin); Xmax=rdsINIReadDouble(ini,"Xmax",Xmax); XGridStep=rdsINIReadDouble(ini,"XGridStep",XGridStep); XNumDecimal=rdsINIReadInt(ini,"XNumDecimal",XNumDecimal); Ymin=rdsINIReadDouble(ini,"Ymin",Ymin); Ymax=rdsINIReadDouble(ini,"Ymax",Ymax); YGridStep=rdsINIReadDouble(ini,"YGridStep",YGridStep); YNumDecimal=rdsINIReadInt(ini,"YNumDecimal",YNumDecimal); } // Если в тексте есть секция "Visuals", загрузить из нее данные if(rdsINIOpenSection(ini,"Visuals")) { BorderColor=(COLORREF)rdsINIReadInt(ini,"BorderColor", (int)BorderColor); FillColor=(COLORREF)rdsINIReadInt(ini,"FillColor", (int)FillColor); PlotBorderColor=(COLORREF)rdsINIReadInt(ini, "PlotBorderColor",(int)PlotBorderColor); PlotFillColor=(COLORREF)rdsINIReadInt(ini,"PlotFillColor", (int)PlotFillColor); LineColor=(COLORREF)rdsINIReadInt(ini,"LineColor", (int)LineColor); LineWidth=(COLORREF)rdsINIReadInt(ini,"LineWidth",LineWidth); str=rdsINIReadString(ini,"Font","",NULL); if(str) rdsFontTextToStruct(str,NULL,&Font); } // Уничтожение вспомогательного объекта rdsDeleteObject(ini); } //========================================= // Функция настройки параметров блока // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" int TSimplePlotData::Setup(void) { RDS_HOBJECT window; BOOL ok; char *str; // Создание окна window=rdsFORMCreate(TRUE,-1,-1,"Простой график"); // Вкладка "Оси" rdsFORMAddTab(window,1,"Оси"); rdsFORMAddEdit(window,1,100, RDS_FORMCTRL_EDIT | RDS_FORMFLAG_LINE,"Шаг записи",50); rdsSetObjectDouble(window,100,RDS_FORMVAL_VALUE,TimeStep); // Текстовая метка без возможности ввода rdsFORMAddEdit(window,1,1,RDS_FORMCTRL_LABEL,"Ось X:",0); // Диапазон (два поля ввода в одной строке) rdsFORMAddEdit(window,1,2,RDS_FORMCTRL_RANGEEDIT,"Диапазон",90); rdsSetObjectDouble(window,2,RDS_FORMVAL_VALUE,Xmin); rdsSetObjectDouble(window,2,RDS_FORMVAL_RANGEMAX,Xmax); rdsFORMAddEdit(window,1,3,RDS_FORMCTRL_EDIT,"Шаг сетки",50); rdsSetObjectDouble(window,3,RDS_FORMVAL_VALUE,XGridStep); // Поле ввода со стрелками увеличения/уменьшения rdsFORMAddEdit(window,1,4, RDS_FORMCTRL_UPDOWN | RDS_FORMFLAG_LINE, "Дробная часть чисел",50); rdsSetObjectInt(window,4,RDS_FORMVAL_VALUE,XNumDecimal); rdsSetObjectInt(window,4,RDS_FORMVAL_UPDOWNMIN,0); rdsSetObjectInt(window,4,RDS_FORMVAL_UPDOWNMAX,5); rdsSetObjectInt(window,4,RDS_FORMVAL_UPDOWNINC,1); // Текстовая метка без возможности ввода rdsFORMAddEdit(window,1,5,RDS_FORMCTRL_LABEL,"Ось Y:",0); rdsFORMAddEdit(window,1,6,RDS_FORMCTRL_RANGEEDIT,"Диапазон",90); rdsSetObjectDouble(window,6,RDS_FORMVAL_VALUE,Ymin); rdsSetObjectDouble(window,6,RDS_FORMVAL_RANGEMAX,Ymax); rdsFORMAddEdit(window,1,7,RDS_FORMCTRL_EDIT,"Шаг сетки",50); rdsSetObjectDouble(window,7,RDS_FORMVAL_VALUE,YGridStep); rdsFORMAddEdit(window,1,8,RDS_FORMCTRL_UPDOWN, "Дробная часть чисел",50); rdsSetObjectInt(window,8,RDS_FORMVAL_VALUE,YNumDecimal); rdsSetObjectInt(window,8,RDS_FORMVAL_UPDOWNMIN,0); rdsSetObjectInt(window,8,RDS_FORMVAL_UPDOWNMAX,5); rdsSetObjectInt(window,8,RDS_FORMVAL_UPDOWNINC,1); // Вкладка "Внешний вид" rdsFORMAddTab(window,2,"Внешний вид"); rdsFORMAddEdit(window,2,9,RDS_FORMCTRL_COLOR,"Цвет рамки блока",50); rdsSetObjectInt(window,9,RDS_FORMVAL_VALUE,(int)BorderColor); rdsFORMAddEdit(window,2,10, RDS_FORMCTRL_COLOR | RDS_FORMFLAG_LINE,"Цвет фона блока",50); rdsSetObjectInt(window,10,RDS_FORMVAL_VALUE,(int)FillColor); rdsFORMAddEdit(window,2,11,RDS_FORMCTRL_COLOR,"Цвет рамки графика и сетки",50); rdsSetObjectInt(window,11,RDS_FORMVAL_VALUE,(int)PlotBorderColor); rdsFORMAddEdit(window,2,12,RDS_FORMCTRL_COLOR,"Цвет фона графика",50); rdsSetObjectInt(window,12,RDS_FORMVAL_VALUE,(int)PlotFillColor); rdsFORMAddEdit(window,2,13,RDS_FORMCTRL_COLOR,"Цвет линии графика",50); rdsSetObjectInt(window,13,RDS_FORMVAL_VALUE,(int)LineColor); rdsFORMAddEdit(window,2,14, RDS_FORMCTRL_UPDOWN | RDS_FORMFLAG_LINE, "Толщина линии графика",50); rdsSetObjectInt(window,14,RDS_FORMVAL_VALUE,LineWidth); rdsSetObjectInt(window,14,RDS_FORMVAL_UPDOWNMIN,0); rdsSetObjectInt(window,14,RDS_FORMVAL_UPDOWNMAX,5); rdsSetObjectInt(window,14,RDS_FORMVAL_UPDOWNINC,1); // Кнопка открытия диалога выбора шрифта rdsFORMAddEdit(window,2,15,RDS_FORMCTRL_FONTSELECT,"Шрифт чисел",0); // Преобразование шрифта в строку и занесение в поле ввода str=rdsStructToFontText(&Font,NULL); rdsSetObjectStr(window,15,RDS_FORMVAL_VALUE,str); rdsFree(str); // Открытие окна ok=rdsFORMShowModalEx(window,NULL); if(ok) { // Нажата кнопка OK - запись параметров обратно в блок Xmin=rdsGetObjectDouble(window,2,RDS_FORMVAL_VALUE); Xmax=rdsGetObjectDouble(window,2,RDS_FORMVAL_RANGEMAX); XGridStep=rdsGetObjectDouble(window,3,RDS_FORMVAL_VALUE); XNumDecimal=rdsGetObjectInt(window,4,RDS_FORMVAL_VALUE); TimeStep=rdsGetObjectDouble(window,100,RDS_FORMVAL_VALUE); Ymin=rdsGetObjectDouble(window,6,RDS_FORMVAL_VALUE); Ymax=rdsGetObjectDouble(window,6,RDS_FORMVAL_RANGEMAX); YGridStep=rdsGetObjectDouble(window,7,RDS_FORMVAL_VALUE); YNumDecimal=rdsGetObjectInt(window,8,RDS_FORMVAL_VALUE); BorderColor=(COLORREF)rdsGetObjectInt(window,9,RDS_FORMVAL_VALUE); FillColor=(COLORREF)rdsGetObjectInt(window,10,RDS_FORMVAL_VALUE); PlotBorderColor=(COLORREF)rdsGetObjectInt(window,11,RDS_FORMVAL_VALUE); PlotFillColor=(COLORREF)rdsGetObjectInt(window,12,RDS_FORMVAL_VALUE); LineColor=(COLORREF)rdsGetObjectInt(window,13,RDS_FORMVAL_VALUE); LineWidth=rdsGetObjectInt(window,14,RDS_FORMVAL_VALUE); // Получение параметров шрифта из строки str=rdsGetObjectStr(window,15,RDS_FORMVAL_VALUE); rdsFontTextToStruct(str,NULL,&Font); } // Уничтожение окна rdsDeleteObject(window); // Возвращаемое значение return ok?RDS_BFR_MODIFIED:RDS_BFR_DONE; } //=========================================/* // Поиск отсчета, соответствующего времени t // (линейный поиск) int TSimplePlotData::FindTimeIndex(double t) { if(Count==0) // Массивы пусты return -1; if(t<=Times[0]) // t раньше начала массива return 0; // Ближайший индекс - 0 // Поиск первого индекса, большего t for(int i=1;i<NextIndex;i++) if(Times[i]>t) // t между i-1 и i { double d1=fabs(t-Times[i-1]), d2=fabs(t-Times[i]); return (d1<d2)?(i-1):i; // Возвращаем ближайший } // Ничего не нашли – значит, t>Times[NextIndex-1] return NextIndex-1; } //========================================= */// Поиск отсчета, соответствующего времени t // (метод деления пополам) int TSimplePlotData::FindTimeIndex(double t) { int L,R; double dl,dr; if(Count==0 || NextIndex==0) // Нет данных в массивах return -1; if(NextIndex==1) // В массиве единственный отсчет return 0; // В массиве по крайней мере два значения – проверяем границы if(t<=Times[0]) // t меньше первого отсчета return 0; if(t>=Times[NextIndex-1]) // t больше последнего отсчета return NextIndex-1; // t - внутри диапазона массива L=0; R=NextIndex-1; while(L<R-1) { int m=(L+R)/2; if(Times[m]<t) L=m; else R=m; } // t лежит между L и R dl=fabs(t-Times[L]), dr=fabs(t-Times[R]); return (dl<dr)?L:R; // Возвращаем ближайший } //=========================================// Всплывающая подсказка // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TSimplePlotData::PopupHint(RDS_PPOPUPHINTDATA hintdata) { int x1,x2,y1,y2,index; double t; char *text,*str_t,*str_v; // Проверка доступа к переменной времени if(Time==NULL || Time->Data==NULL) { rdsSetHintText("ОШИБКА: в схеме нет переменной DynTime"); return; } // Определение абсолютных координат поля графика x1=hintdata->Left+Gr_x1; x2=hintdata->Left+Gr_x2; y1=hintdata->Top+Gr_y1; y2=hintdata->Top+Gr_y2; // Если курсор мыши не попадает в поле графика, // подсказку выводить не нужно if(hintdata->x<x1 || hintdata->x>x2 || hintdata->y<y1 || hintdata->y>y2) return; // Преобразование экранной горизонтальной координаты // в значение времени согласно масштабу графика t=(hintdata->x-x1)*(Xmax-Xmin)/(x2-x1)+Xmin; // Поиск отсчета, соответствующего этому моменту времени index=FindTimeIndex(t); if(index<0) return; // Ошибка – отчет не найден // Преобразование времени и значения отсчета в строки str_t=rdsDtoA(Times[index],-1,NULL); str_v=rdsDtoA(Values[index],-1,NULL); // Формирование текста подсказки text=rdsDynStrCat("Время: ",str_t,FALSE); rdsAddToDynStr(&text,"\nЗначение: ",FALSE); rdsAddToDynStr(&text,str_v,FALSE); // Установка текста подсказки rdsSetHintText(text); // Освобождение динамических строк rdsFree(text); rdsFree(str_t); rdsFree(str_v); // Изменение параметров подсказки таким образом, чтобы при // смещении курсора на одну точку она вывелась снова hintdata->HZLeft=hintdata->x; hintdata->HZTop=hintdata->y; hintdata->HZWidth=hintdata->HZHeight=1; // Задержка гашения подсказки – одна минута hintdata->HideTimeout=60000; } //=========================================