1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/rewine-mfcplot

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

MFCplot: программа для построения графиков математических функций

Описание программы

  • Название: MFCplot.

  • Назначение: Программа для построения графиков математических функций.

  • Функции:

    • Построение графиков следующих типов функций:
      • Обычные функции;
      • Функции в полярных координатах;
      • Параметрические уравнения;
      • Ввод данных точек напрямую.
    • Возможность удаления графика выбранной функции.
    • Одновременное построение нескольких графиков функций на одной системе координат.
    • Отображение осей, сетки и значений шкалы.
    • Выбор разных цветов линий для разных графиков.
    • При наведении курсора на точку графика отображается её координата.
    • Перемещение графика с помощью мыши.
    • Масштабирование графика (увеличение/уменьшение), а также возможность задать диапазон отображения и автоматическое масштабирование.
    • Для обычных функций можно задать диапазон значений x, который будет изменяться вместе с диапазоном отображения.
    • Строка состояния отображает текущую позицию курсора, а двойное нажатие на неё показывает точное положение курсора.
    • Сериализация и десериализация важных данных.
  • Ограничения:

    • Проблемы с отображением функций, которые имеют резкие изменения или приближаются к бесконечности, например, floor(x) и tan(x).

Интерфейс Программа имеет удобный интерфейс, позволяющий легко настраивать параметры графиков и просматривать результаты.

Структура программы

  • Процесс работы: Пользователь вводит информацию о функции, и программа строит график этой функции. Также пользователь может изменять настройки через меню и панель инструментов.
  • Принцип работы: Программа вычисляет значения функции в заданных точках, а затем соединяет соседние точки прямыми линиями. Если количество точек достаточно велико, то график можно считать приближением к кривой функции.
  • Файлы программы: Все исходные файлы программы представлены в виде списка.
  • CalculatorFunc.cpp: Этот файл содержит функцию CalcEquation, которая принимает уравнение в качестве аргумента и возвращает значение функции при заданном значении неизвестного.
  • FuncData.cpp: Класс FuncData используется для хранения информации о функциях. Он включает в себя такие данные, как тип функции, уравнение, диапазон значений, шаг и другие параметры.
  • mfcplotDoc.cpp: В этом файле хранятся настройки программы и информация о функциях. Здесь также реализованы функции для управления графиками и настройками. afx_msg void OnNearpointMenu(); // отображает ли он ближайшую точку afx_msg void OnAutorangeMenu(); // автоматически регулирует отображаемый диапазон, чтобы точно отобразить полное изображение функции afx_msg void OnDelfunconeMenu(); // удаляет одну функцию afx_msg void OnDelallMenu(); // удаляет все функции

mfcplotView.cpp Введение

 Логика построения графиков реализуется здесь. Основное содержание следующее:

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
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwineee%2Fmfcplot.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwineee%2Fmfcplot?ref=badge_large)

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Программа для построения графиков математических функций с помощью MFC. Развернуть Свернуть
MIT
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/rewine-mfcplot.git
git@api.gitlife.ru:oschina-mirror/rewine-mfcplot.git
oschina-mirror
rewine-mfcplot
rewine-mfcplot
master