MFCplot: программа для построения графиков математических функций
Описание программы
Название: MFCplot.
Назначение: Программа для построения графиков математических функций.
Функции:
Ограничения:
Интерфейс Программа имеет удобный интерфейс, позволяющий легко настраивать параметры графиков и просматривать результаты.
Структура программы
Логика построения графиков реализуется здесь. Основное содержание следующее:
public:
double m_Xmin, m_Xmax, m_Ymin, m_Ymax;//диапазон отображения функций
int nTop, nButton, nLeft, nRight;//соответствующий логический диапазон координат
int isMoving;//состояние перетаскивания 0 не перетаскивается 1 режим перетаскивания 2 перетаскивание
double tmp_Xmin, tmp_Xmax, tmp_Ymin, tmp_Ymax;
//в режиме перетаскивания, когда левая кнопка мыши нажата, запишите начальную точку диапазона отображения
CPoint m_posStart;
//в режиме перетаскивания, когда нажата левая кнопка мыши, запишите координаты начальной точки мыши
//можно рассчитать изменение диапазона отображения на основе смещения координат мыши
В функции LPxtoFPx(x) означает преобразование функции координат x в логические координаты pDC _x, принцип преобразования координат функции xmin, xmax в логические координаты nLeft, nRight (следующая функция будет предоставлена) в соответствии с соотношением.
double CmfcplotView::LPxtoFPx(int x) {
return m_Xmin + (1.0 * x - nLeft) * (m_Xmax - m_Xmin) / (1.0 * nRight - nLeft);
}
После реализации преобразования координат можно приступить к построению графика.
void CmfcplotView::OnDraw(CDC* pDC)
{
CmfcplotDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
m_Xmin = pDoc->m_Xmin;//сохраняем экстремальные значения в Doc
m_Xmax = pDoc->m_Xmax;
m_Ymin = pDoc->m_Ymin;
m_Ymax = pDoc->m_Ymax;
CRect rect;
GetClientRect(&rect);//получаем область просмотра
nTop = (int)round(rect.bottom * 0.1); //изображение функции не занимает всю область просмотра
nButton = (int)round(rect.bottom * 0.9);
nLeft = (int)round(rect.right * 0.1);
nRight = (int)round(rect.right * 0.9);
if (pDoc->m_WillShowEdge) {//рисуем рамку
pDC->MoveTo(nLeft, nTop);
pDC->LineTo(nLeft, nButton);
pDC->LineTo(nRight, nButton);
pDC->LineTo(nRight, nTop);
pDC->LineTo(nLeft, nTop);
}
//рисуем информацию о координатах x
int nX,nY;
bool BIGX = abs(m_Xmin) > 100 || abs(m_Xmax) > 100;//значение координаты x большое, тогда отметка более редкая
for (nX = nLeft; nX < nRight; nX += (BIGX ? 100 : 50)) { //каждые 100/50 пикселей одна отметка
CRect textRect(nX - (BIGX ? 50 : 25), nButton + 1, nX + (BIGX ? 50 : 25), nButton + 20);//область отображения
CString xInfo;
xInfo.Format(_T("%.2f"),LPxtoFPx(nX));
pDC->DrawText(xInfo, &textRect, DT_SINGLELINE | DT_CENTER);
//одна строка, сверху вниз слева направо выравнивание по центру
}
if (nX - nRight <= (BIGX ? 50 : 25)) {//последний x, расстояние до предыдущего слишком близко, не отображается
CRect textRect(nRight, nButton + 1, nRight + 50, nButton + 20);
CString xInfo;
xInfo.Format(_T("%.2f"),m_Xmax);
pDC->DrawText(xInfo, &textRect, DT_SINGLELINE | DT_LEFT | DT_TOP);
}
//координатная информация y
for (nY = nButton - 50; nY > nTop; nY -= 50) {
CRect textRect(nLeft - 200, nY-10, nLeft - 3, nY + 10);
CString yInfo;
yInfo.Format(_T("%.2f"), LPytoFPy(nY));
pDC->DrawText(yInfo, &textRect, DT_SINGLELINE | DT_RIGHT);
}
if (nTop - nY <= 25) {
CRect textRect(nLeft - 200, nTop - 10, nLeft - 3, nTop + 10);
CString yInfo;
yInfo.Format(_T("%.2f"),m_Ymax);
pDC->DrawText(yInfo, &textRect, DT_SINGLELINE | DT_BOTTOM | DT_RIGHT);
}
// отображение сетки
if (pDoc->m_WillShowGrid) {
CPen pen(PS_DOT, 1, RGB(100, 100, 100)); //создать перо, пунктирная линия, и настроить цвет координат серый
CPen *pOldPen = (CPen *)pDC->SelectObject(&pen);
for (nX = nLeft+50; nX < nRight; nX += 50) {
pDC->MoveTo(nX, nTop);
pDC->LineTo(nX, nButton);
}
for (nY = nButton - 50; nY > nTop; nY -= 50) {
pDC->MoveTo(nLeft, nY);
pDC->LineTo(nRight, nY);
}
pDC->SelectObject(pOldPen);
}
// отображение оси координат
if (pDoc->m_WillShowAxis) {
CPen pen(PS-SOLID, 2, RGB(0, 0, 0));
CPen* pOldPen = (CPen*)pDC->SelectObject(&pen);
int oX = FPxtoLPx(0);
int oY = FPytoLPy(0);
bool showY = oX >= nLeft && oX <= nRight;
bool showX = oY >= nTop && oY <= nButton;//судить, находится ли ось x, y в диапазоне
if (showX) {
pDC->MoveTo(nLeft - 10, oY);
pDC->LineTo(nRight + 10, oY);
}
if (showY) {
pDC->MoveTo(oX, nButton + 10);
pDC->LineTo(oX, nTop - 10);
}
if (showX && showY) {
pDC->TextOutW(oX + 1, oY + 1, _T("O"));
}
if (showX) {
pDC->MoveTo(nRight + 10, oY);
pDC->LineTo(nRight + 5, oY + 5);
*Обратите внимание, что в тексте запроса могут быть ошибки или неточности, которые невозможно исправить без контекста.* Данный текст написан на языке C++.
**pDC->MoveTo(nRight + 10, oY);
pDC->LineTo(nRight + 5, oY - 5);
pDC->TextOutW(nRight + 10, oY, _T("X轴"));**
*pDC* перемещает перо в точку с координатами *(nRight + 10, oY)*. Затем перо перемещается в точку *(nRight + 5, oY – 5)* и проводится линия. После этого в точке с координатами *(nRight + 10, oY)* выводится текст *«X轴»*.
**if (showY) {
pDC->MoveTo(oX, nTop - 10);
pDC->LineTo(oX - 5, nTop - 5);
pDC->MoveTo(oX, nTop - 10);
pDC->LineTo(oX + 5, nTop - 5);
pDC->TextOutW(oX + 5, nTop - 10, _T("Y轴"));}**
Если переменная *showY* равна *true*, то перо перемещается в точку с координатами *(oX, nTop – 10)*. Далее проводится линия до точки *(oX – 5, nTop – 5)*. Потом перо возвращается в начальную точку и проводится вторая линия до точки *(oX + 5, nTop – 5)*. В точке с координатами *(oX + 5, nTop – 10)* выводится текст *«Y轴»* .
**POSITION p = pDoc->m_List.GetHeadPosition();**
Переменной *p* присваивается значение, полученное при помощи метода *GetHeadPosition()* объекта *m_List* переменной *pDoc*.
**int showTop = nTop;**
Целочисленной переменной *showTop* присваивается значение переменной *nTop*.
**while (p != nullptr) {**
Пока *p* не равно *nullptr*, выполняется код внутри цикла.
**bool shouldMov = true;//一段曲线第一个点MoveTo,其他都是LineTo**
Логической переменной *shouldMov* присваивается значение *true*.
**FuncData* tmpFD = (FuncData*)pDoc->m_List.GetNext(p);**
В переменную *tmpFD* записывается значение, полученное методом *GetNext()* объекта *m_List* переменной *pDoc*, которое предварительно приводится к типу *FuncData*.
**CPen pen(tmpFD->m_penType, tmpFD->m_penWidth, tmpFD->m_color);**
Создаётся объект класса *CPen*, которому в качестве параметров передаются значения полей *m_penType*, *m_penWidth* и *m_color* объекта *tmpFD*.
**CPen* pOldPen = (CPen*)pDC->SelectObject(&pen);**
Методом *SelectObject()* объекта *pDC* создаётся новый объект класса *CPen*. Адрес созданного объекта записывается в переменную *pOldPen*.
**if (tmpFD->FuncCas == CAS_NORMAL) {//动态X坐标模式下,普通函数x范围与视图不同时自动同步**
Если поле *FuncCas* объекта *tmpFD* равно *CAS_NORMAL*, то выполняется следующий код.
**if (pDoc->m_ForceXrange && isMoving!=2)**
Если у объекта *pDoc* есть поле *m_ForceXrange* и значение переменной *isMoving* не равно 2, то выполняется следующий код.
**if (tmpFD->minX != m_Xmin || tmpFD->maxX != m_Xmax) {**
Проверяется, равны ли поля *minX* и *maxX* объекта *tmpFD* полям *m_Xmin* и *m_Xmax* соответственно. Если хотя бы одно из условий не выполняется, то выполняется следующий код.
**tmpFD->minX = m_Xmin;
tmpFD->maxX = m_Xmax;
tmpFD->CalcList();}**
Полям *minX* и *maxX* объекта *tmpFD* присваиваются значения полей *m_Xmin* и *m_Xmax*. Затем вызывается метод *CalcList()* объекта *tmpFD*.
**for (auto dot : tmpFD->vetPoint) {**
Выполняется цикл для каждого элемента коллекции *vetPoint* объекта *tmpFD*. На каждой итерации цикла в переменную *dot* записывается текущий элемент коллекции.
**if (dot.first < m_Xmin || dot.first > m_Xmax || dot.second < m_Ymin || dot.second > m_Ymax || dot.second != dot.second) {**
Проверяются условия: меньше ли значение поля *first* элемента *dot* значения поля *m_Xmin*, больше ли оно значения поля *m_Xmax*, меньше ли значение поля *second* элемента *dot* значения поля *m_Ymin*, больше ли оно значения поля *m_Ymax* и равно ли значение поля *second* элемента *dot* самому себе. Если хотя бы одно условие выполняется, то должна выполниться следующая инструкция.
**shouldMov = true;
continue;}**
Значение логической переменной *shouldMov* устанавливается в *true* и происходит переход к следующей итерации цикла.
**if (shouldMov) {**
Если значение логической переменной *shouldMov* равно *true*, то выполняется следующий код.
**pDC->MoveTo(FPxtoLPx(dot.first), FPytoLPy(dot.second));**
Перо перемещается в точку с декартовыми координатами *(FPxtoLPx(dot.first), FPytoLPy(dot.second))*.
**else
pDC->LineTo(FPxtoLPx(dot.first), FPytoLPy(dot.second));}**
Иначе выполняется метод *LineTo()* объекта *pDC*, перо проводит линию до указанной точки.
**pDC->MoveTo(nRight+5, showTop);//显示图例**
Перо перемещается в точку с координатами *(nRight + 5, showTop)*.
**pDC->LineTo(rect.right, showTop);**
Проводится линия до точки с координатами *(rect.right, showTop)*, где *rect* — это структура типа *CRect*.
**showTop += 5;**
Значение переменной *showTop* увеличивается на 5.
**if (tmpFD->FuncCas == CAS_NORMAL)**
Если поле *FuncCas* объекта *tmpFD* равно *CAS_NORMAL*, то выполняется следующий код.
**pDC->TextOutW(nRight + 5, showTop, _T("f(x)=")+tmpFD->m_Equation);**
Выводится текст *«f(x)=»* и значение поля *m_Equation* объекта *tmpFD*, после чего курсор устанавливается в точку с координатами *(nRight + 5, showTop)*.
**else if (tmpFD->FuncCas == CAS_POLAR)**
Если поле *FuncCas* объекта *tmpFD* равно *CAS_POLAR*, то выполняется следующий код.
**pDC->TextOutW(nRight + 5, showTop, _T("r(t)=") + tmpFD->m_Equation);**
Выводится текст *«r(t)=»* и значение поля *m_Equation* объекта *tmpFD*, после чего курсор устанавливается в точку с координатами *(nRight + 5, showTop)*.
**else if (tmpFD->FuncCas == CAS_TWO)**
Если поле *FuncCas* объекта *tmpFD* равно *CAS_TWO*, то выполняется следующий код.
**{**
**pDC->TextOutW(nRight + 5, showTop, _T("x(t)=") + tmpFD->m_Equation);
showTop += 20;**
Выводится текст *«x(t)=»* и значение поля *m_Equation* объекта *tmpFD*, после чего курсор устанавливается в точку с координатами *(nRight + 5, showTop)*. Значение переменной *showTop* увеличивается на 20.
**pDC->TextOutW(nRight + 5, showTop, _T("y(t)=") + tmpFD->GetEquation2());}**
Выводится текст *«y(t)=»* и результат вызова метода *GetEquation2()* объекта *tmpFD*, после чего курсор устанавливается в точку с координатами *(nRight + 5, showTop)*.
**else if (tmpFD->FuncCas == CAS_DATA)**
Если поле *FuncCas* объекта *tmpFD* равно *CAS_DATA*, то выполняется следующий код.
**pDC->TextOutW(nRight + 5, showTop, _T("y(t)=") + tmpFD->m_Equation);}**
Выводится текст *«y(t)=»* и значение поля *m_Equation* объекта *tmpFD*, после чего курсор устанавливается в точку с координатами *(nRight + 5, showTop)*.
**showTop += 25;**
Значение переменной *showTop* увеличивается на 25.
**pDC->SelectObject(pOldPen);**
Происходит возврат к объекту, который был выбран методом *SelectObject()*.
Данный текст представляет собой фрагмент кода, связанного с отрисовкой графиков функций. **Функции DrawText: объяснение**
**VC двойная буферизация графики: введение в технологию**
*Ссылки:*
- Блог mxy на CSDN. Статья «Объяснение функций DrawText».
- Блог oceanlucy на CSDN. Статья «Введение в технологию двойной буферизации графики VC».
*Литература:*
1. Янь Гуанвэй, Пэн Вэнь, Сюй Линьцянь. Основываясь на примерах, обучаем программированию на Visual C++. Издательство Университета Цинхуа, Пекин, 2012.
2. Чжан Сяомэнь. Технология разработки приложений VC++ 2010. Издательство Машиностроительной промышленности, Пекин, 2013.
## License
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fwineee%2Fmfcplot?ref=badge_large)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )