График функции sin(x)

График функции sin(x)

Задача: Для x∈[0;7] построить график функции y=sin(x).

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

График функции y=sin(x) имеет одинаковое отклонение как вверх, так и вниз относительно оси x. Поэтому за начало отсчета принимается точка со смещением 0 по горизонтали и со смещением 1/2 высоты по вертикали.

Смещение графика по горизонтали и вертикали

Координата X меняется в пределах [0;7], координата Y — в пределах [-1;1].

Величины MAX_X и MAX_Y представляют собой область допустимых значений по осям координат:

  • MAX_X = maxX — minX = 7;
  • MAX_Y = maxY — minY = 2.

Смещение графика функции представляет собой положение первой точки относительно начала отсчета левого верхнего угла:

  • смещение по оси X: OffsetX = minX * width / MAX_X;
  • смещение по оси Y: OffsetY = maxY * height / MAX_Y

В данном случае смещение по оси x OffsetX = 0, поскольку значение minX = 0 представляет собой минимальное значение координаты x из области допустимых значений.

.

Значение maxY = 1 представляет собой максимальное значение координаты y из области допустимых значений. Рассматривается именно максимальное значение, поскольку за начало отсчета принят левый верхний угол окна, и координата y увеличивается по направлению вниз.

Для того чтобы график функции разместился в окне, необходимо рассчитать масштабные коэффициенты по осям. Масштабный коэффициент представляет собой отношение размера окна к области допустимых значений функции.

  • масштабный коэффициент по оси X: ScaleX = width / MAX_X;
  • масштабный коэффициент по оси Y: ScaleY = height / MAX_Y.

Вычисление координат следующей точки (x;y) графика в окне будет осуществляться по формулам:

  • координата X = OffsetX + x * ScaleX;
  • координата Y = OffsetY + y * ScaleY,

где (x;y) — координаты точки, полученные из функции y=sin(x) для следующей координаты x.

Реализация на C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <windows.h>
#include <tchar.h>   
#include <math.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam);
const int NUM = 70; // количество точек
double* x, * y; // массивы данных x и y
// Задание исходных данных для графика
// (двумерный массив, может содержать несколько рядов данных)
void getData(int n)
{
    for (int i = 0; i < n; i++)
    {
        x[i] = (double)i * 0.1;
        y[i] = sin(x[i]);
    }
    return;
}
// Функция рисования графика
void DrawGraph(HDC hdc, RECT rectClient, int n)
{
    double OffsetY, OffsetX;
    double MAX_X, MAX_Y;
    double ScaleX, ScaleY;
    double min, max;
    int height, width;
    int X, Y; // координаты в окне (в px)
    HPEN hpen;
    height = rectClient.bottom - rectClient.top;
    width = rectClient.right - rectClient.left;
    // Область допустимых значений X
    min = x[0];
    max = x[0];
    for (int i = 0; i < n; i++)
    {
        if (x[i] < min) min = x[i];
        if (x[i] > max) max = x[i];
    }
    double temp = max - min;
    MAX_X = max - min;
    OffsetX = min * width / MAX_X; // смещение X
    ScaleX = (double)width / MAX_X; // масштабный коэффициент X
    // Область допустимых значений Y
    min = y[0];
    max = y[0];
    for (int i = 0; i < n; i++)
    {
        if (y[i] < min) min = y[i];
        if (y[i] > max) max = y[i];
    }
    MAX_Y = max - min;
    OffsetY = max * height / (MAX_Y); // смещение Y
    ScaleY = (double)height / MAX_Y; // масштабный коэффициент Y
    // Отрисовка осей координат
    hpen = CreatePen(PS_SOLID, 0, 0); // черное перо 1px
    SelectObject(hdc, hpen);
    MoveToEx(hdc, 0, OffsetY, 0); // перемещение в точку (0;OffsetY)
    LineTo(hdc, width, OffsetY); // рисование горизонтальной оси
    MoveToEx(hdc, OffsetX, 0, 0); // перемещение в точку (OffsetX;0)
    LineTo(hdc, OffsetX, height); // рисование вертикальной оси (не видна)
    DeleteObject(hpen); // удаление черного пера
    // Отрисовка графика функции
    int color = 0xFF; // красное перо для ряда данных
    hpen = CreatePen(PS_SOLID, 2, color); // формирование пера 2px
    SelectObject(hdc, hpen);        // выбор пера
    X = (int)(OffsetX + x[0] * ScaleX); // начальная точка графика
    Y = (int)(OffsetY - y[0] * ScaleY);
    MoveToEx(hdc, X, Y, 0); // перемещение в начальную точку
    for (int i = 1; i < n; i++)   // точка 0 из массивов уже использована
    {
        X = OffsetX + x[i] * ScaleX;
        Y = OffsetY - y[i] * ScaleY;
        LineTo(hdc, X, Y);
    }
    color = color << 8; // изменение цвета пера для следующего ряда
    DeleteObject(hpen); // удаление текущего пера
}
// Главная функция
int WINAPI WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASS w;
    x = new double[NUM];  // создаем массивы для координат x и y
    y = new double[NUM];
    getData(NUM); // задание исходны данных
    memset(&w, 0, sizeof(WNDCLASS));
    w.style = CS_HREDRAW | CS_VREDRAW;
    w.lpfnWndProc = WndProc;
    w.hInstance = hInstance;
    w.hbrBackground = CreateSolidBrush(0x00FFFFFF);
    w.lpszClassName = _T("My Class");
    RegisterClass(&w);
    hwnd = CreateWindow(_T("My Class"), _T("График функции"),
        WS_OVERLAPPEDWINDOW,
        500, 300, 500, 380, NULLNULL,
        hInstance, NULL);
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    delete[] x;
    delete[] y;
    return msg.wParam;
}
// Оконная функция
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    switch (Message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        DrawGraph(hdc, ps.rcPaint, NUM); // построение графика
        // Вывод текста y=sin(x)
        SetTextColor(hdc, 0x00FF0000); // синий цвет букв
        TextOut(hdc, 10, 20, _T("y=sin(x)"), 8);
        EndPaint(hwnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, Message, wparam, lparam);
    }
    return 0;
}

Результат выполнения

Примечание: Для корректной сборки приложения используется многобайтовая кодировка.

13 комментариев к “График функции sin(x)”

  1. LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam) — где и как использовать? Простое размещение кода программы внутри Windows- приложения, которое генерируется транслятором невозможно.

    1. Елена

      Затрудняюсь ответить — использовать там, где компилируется.
      Я создала классическое приложение и посмотрела тип возвращаемого значения, который там генерирует сам Visual Studio

  2. Компилятор MS Visual Studio 22 выдает 11 ошибок на текст данной программы. Начиная со строки height = rectClient.bottom — rectClient.top; -требуется точка с запятой..

    1. Елена

      По этой информации не смогу помочь разобраться с ошибками. К сожалению, иногда приходится немного изменять код, чтобы он компилироваться в нужном компиляторе.

      1. Сообщения об ошибках:Серьезность Код Описание Проект Файл Строка Состояние подавления
        Ошибка (активно) E0065 требуется точка с запятой ";" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 40
        Ошибка (активно) E0065 требуется точка с запятой ";" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 41
        Ошибка (активно) E0065 требуется точка с запятой ";" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 50
        Ошибка (активно) E0065 требуется точка с запятой ";" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 51
        Ошибка (активно) E0065 требуется точка с запятой ";" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 65
        Ошибка (активно) E0018 требуется круглая скобка ")" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 83
        Ошибка (активно) E0065 требуется точка с запятой ";" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 88
        Ошибка (активно) E0513 значение типа "LONG (__stdcall *)(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)" нельзя присвоить сущности типа "WNDPROC" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 105
        Ошибка (активно) E0513 значение типа "const char *" нельзя присвоить сущности типа "LPCWSTR" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 108
        Ошибка (активно) E0167 аргумент типа "const char *" несовместим с параметром типа "LPCWSTR" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 110
        Ошибка (активно) E0167 аргумент типа "const char *" несовместим с параметром типа "LPCWSTR" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 136

        1. Елена

          Вот код, который откомпилировался в VS 2019. К сожалению, отследить за развитием компиляторов очень сложно.

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          46
          47
          48
          49
          50
          51
          52
          53
          54
          55
          56
          57
          58
          59
          60
          61
          62
          63
          64
          65
          66
          67
          68
          69
          70
          71
          72
          73
          74
          75
          76
          77
          78
          79
          80
          81
          82
          83
          84
          85
          86
          87
          88
          89
          90
          91
          92
          93
          94
          95
          96
          97
          98
          99
          100
          101
          102
          103
          104
          105
          106
          107
          108
          109
          110
          111
          112
          113
          114
          115
          116
          117
          118
          119
          120
          121
          122
          123
          124
          125
          126
          127
          128
          129
          130
          131
          132
          133
          134
          135
          136
          137
          138
          139
          140
          141
          142
          #include <windows.h>
          #include <math.h>
          #include <tchar.h>
          const int NUM = 70; // количество точек
          LONG WINAPI WndProc(HWNDUINTWPARAMLPARAM);
          double** x; // массив данных
                // Задание исходных данных для графика
                // (двумерный массив, может содержать несколько рядов данных)
          double** getData(int n)
          {
              double** f;
              f = new double* [2];
              f[0] = new double[n];
              f[1] = new double[n];
              for (int i = 0; i < n; i++)
              {
                  double x = (double)i * 0.1;
                  f[0][i] = x;
                  f[1][i] = sin(x);
              }
              return f;
          }
          // Функция рисования графика
          void DrawGraph(HDC hdc, RECT rectClient,
              double** x, // массив данных
              int n, // количество точек
              int numrow = 1) // количество рядов данных (по умолчанию 1)
          {
              double OffsetY, OffsetX;
              double MAX_X, MAX_Y;
              double ScaleX, ScaleY;
              double min, max;
              int height, width;
              int X, Y; // координаты в окне (в px)
              HPEN hpen;
              height = rectClient.bottom — rectClient.top;
              width = rectClient.right — rectClient.left;
              // Область допустимых значений X
              min = x[0][0];
              max = x[0][0];
              for (int i = 0; i < n; i++)
              {
                  if (x[0][i] < min) min = x[0][i];
                  if (x[0][i] > max) max = x[0][i];
              }
              double temp = max — min;
              MAX_X = max — min;
              OffsetX = min * width / MAX_X; // смещение X
              ScaleX = (double)width / MAX_X; // масштабный коэффициент X
                              // Область допустимых значений Y
              min = x[1][0];
              max = x[1][0];
              for (int i = 0; i < n; i++)
              {
                  for (int j = 1; j <= numrow; j++)
                  {
                      if (x[j][i] < min) min = x[j][i];
                      if (x[j][i] > max) max = x[j][i];
                  }
              }
              MAX_Y = max — min;
              OffsetY = max * height / (MAX_Y); // смещение Y
              ScaleY = (double)height / MAX_Y; // масштабный коэффициент Y
                               // Отрисовка осей координат
              hpen = CreatePen(PS_SOLID, 0, 0); // черное перо 1px
              SelectObject(hdc, hpen);
              MoveToEx(hdc, 0, OffsetY, 0); // перемещение в точку (0;OffsetY)
              LineTo(hdc, width, OffsetY); // рисование горизонтальной оси
              MoveToEx(hdc, OffsetX, 0, 0); // перемещение в точку (OffsetX;0)
              LineTo(hdc, OffsetX, height); // рисование вертикальной оси (не видна)
              DeleteObject(hpen); // удаление черного пера
                         // Отрисовка графика функции
              int color = 0xFF; // красное перо для первого ряда данных
              for (int j = 1; j <= numrow; j++)
              {
                  hpen = CreatePen(PS_SOLID, 2, color); // формирование пера 2px
                  SelectObject(hdc, hpen);
                  X = (int)(OffsetX + x[0][0] * ScaleX); // начальная точка графика
                  Y = (int)(OffsetY — x[j][0] * ScaleY);
                  MoveToEx(hdc, X, Y, 0); // перемещение в начальную точку
                  for (int i = 0; i < n; i++)
                  {
                      X = OffsetX + x[0][i] * ScaleX;
                      Y = OffsetY — x[j][i] * ScaleY;
                      LineTo(hdc, X, Y);
                  }
                  color = color << 8; // изменение цвета пера для следующего ряда
                  DeleteObject(hpen); // удаление текущего пера
              }
          }
          // Главная функция
          int WINAPI WinMain(HINSTANCE hInstance,
              HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
          {
              HWND hwnd;
              MSG msg;
              WNDCLASS w;
              x = getData(NUM); // задание исходны данных
              memset(&w, 0, sizeof(WNDCLASS));
              w.style = CS_HREDRAW | CS_VREDRAW;
              w.lpfnWndProc = WndProc;
              w.hInstance = hInstance;
              w.hbrBackground = CreateSolidBrush(0x00FFFFFF);
              w.lpszClassName = __T("My Class");
              RegisterClass(&w);
              hwnd = CreateWindowW(__T("My Class"), __T("График функции"),
                  WS_OVERLAPPEDWINDOW,
                  500, 300, 500, 380, NULLNULL,
                  hInstance, NULL);
              ShowWindow(hwnd, nCmdShow);
              UpdateWindow(hwnd);
              while (GetMessage(&msg, NULL, 0, 0))
              {
                  TranslateMessage(&msg);
                  DispatchMessage(&msg);
              }
              return msg.wParam;
          }
          // Оконная функция
          LONG WINAPI WndProc(HWND hwnd, UINT Message,
              WPARAM wparam, LPARAM lparam)
          {
              HDC hdc;
              PAINTSTRUCT ps;
              switch (Message)
              {
              case WM_PAINT:
                  hdc = BeginPaint(hwnd, &ps);
                  DrawGraph(hdc, ps.rcPaint, x, NUM); // построение графика
                                    // Вывод текста y=sin(x)
                  SetTextColor(hdc, 0x00FF0000); // синий цвет букв
                  TextOut(hdc, 10, 20, __T("y=sin(x)"), 8);
                  EndPaint(hwnd, &ps);
                  break;
              case WM_DESTROY:
                  PostQuitMessage(0);
                  break;
              default:
                  return DefWindowProc(hwnd, Message, wparam, lparam);
              }
              return 0;
          }

          P.S. На сайте имеет место автозамена символа "минус" на "тире".

          1. На этот текст компилятор выдал только одну ошибку:
            Серьезность Код Описание Проект Файл Строка Состояние подавления
            Ошибка (активно) E0513 значение типа "LONG (__stdcall *)(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)" нельзя присвоить сущности типа "WNDPROC" ConsoleApplication82 C:\Users\Mars\source\repos\ConsoleApplication82\ConsoleApplication82\ConsoleApplication82.cpp 101

          2. Елена

            Не консольное приложение случайно создаёте?

          3. Неважно какое приложение, консольное или нет, ошибка выдается одна и та же! Как все-таки запустить программу?

          4. Елена

            Попробуйте использовать заголовок вида

            1
            LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)

            который предлагает Visual Studio по умолчанию при создании оконного приложения

          5. Руслан

            Сделайте приведение типа
            w.lpfnWndProc = (WNDPROC)WndProc;

  3. Angry Birds

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

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх