Windows h c графика в консоли функции

У меня есть код, находящий значения конхоиды Никомеда, и я хотел бы реализовать отрисовку графика по точкам(параллельно с вычислением значений). Я пытался сделать отрисовку прямо в консоли, но в моей

То, что вы рисуете напрямую в окне консоли, затирается стандартным обработчиком отрисовки conhost при следующем обновлении окна (как при выводе нового текста в консоль, так и при изменении, например, размеров ее окна). Переопределить этот обработчик нельзя, но можно отобразить свое окно поверх консоли, как показано в примере ниже. Кроме того, код рисования графика в вопросе рисует график по отдельным точкам, я переделал его с SetPixel на MoveTo/LineTo, чтобы он выглядел непрерывным.

#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNINGS
#include <math.h>
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <string>
#include <clocale>
#include <iostream>

WCHAR szTitle[] = L"Conhoid";
WCHAR szWindowClass[] = L"ConhoidWndClass";

//координаты окна
const int WND_X = 300;
const int WND_Y = 50;
const int WND_W = 400;
const int WND_H = 400;

ATOM MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI Draw(HDC hdc);

DWORD WINAPI Run(LPVOID lpThreadParameter)
{   
    HINSTANCE hInstance = GetModuleHandle(NULL);
    HWND hCon = GetConsoleWindow();
    RECT rc;
    MSG msg;

    MyRegisterClass(hInstance); //инициализация класса окна 
    GetWindowRect(hCon, &rc); //получаем положение окна консоли     

    //создаем окно
    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_BORDER, rc.left + WND_X, rc.top + WND_Y, WND_W, WND_H, hCon,
        nullptr, hInstance, nullptr); 

    SetWindowLong(hWnd, GWL_STYLE, 0); //убираем строку заголовка

    if (hWnd == nullptr)
    {
        printf("Error CreateWindown");
        return 0;
    }

    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd); 

    //запуск цикла обработки сообщений
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);

    return RegisterClassExW(&wcex);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    switch (message)
    {   
    case WM_PAINT:

        PAINTSTRUCT ps;
        hdc = BeginPaint(hWnd, &ps);
        Draw(hdc);
        EndPaint(hWnd, &ps);

    break;  
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

void Conhoid(void) {
    float h = M_PI / 180, x, y, a = 20, r = 40;
    float f = -3;
    while (f <= 3) {
        x = a + r * cos(f);
        y = a * sin(f) / cos(f) + r * sin(f);
        printf("x = %f     y = %fn", x, y);
        f += h;
    }
}

DWORD WINAPI Stream(LPVOID lparoun) {
    Conhoid();
    return 0;
}

DWORD WINAPI Draw(HDC hdc) {        

    float h = M_PI / 180, x, y, a = 20, r = 40;
    float x0, y0;
    float f = -3;

    x = a + r * cos(f);
    y = a * sin(f) / cos(f) + r * sin(f);
    x0 = x;
    y0 = y;
    SelectObject(hdc, GetStockObject(WHITE_PEN));
    MoveToEx(hdc, 300 + x, 50 + y, nullptr);

    while (f <= 3) {
        x = a + r * cos(f);
        y = a * sin(f) / cos(f) + r * sin(f);

        if (abs(y - y0) < 1000) {
            LineTo(hdc, 300 + x, 50 + y);
        }
        else {
            MoveToEx(hdc, 300 + x, 50 + y, nullptr);
        }

        f += h;
        x0 = x; y0 = y;
    }

    return 0;
}

int main()
{
    setlocale(LC_ALL, "RUSSIAN");
    DWORD dwStream = 1;
    HANDLE hStream, drawStream;
    hStream = CreateThread(NULL, 0, Stream, &dwStream, 0, &dwStream);
    drawStream = CreateThread(NULL, 0, Run, &dwStream, 0, &dwStream);
    if (hStream == NULL)
        printf("Поток не запущенn");
    else {
        printf("Поток завершилсяn");
        printf("n");
        CloseHandle(hStream);
    }
    _getch();
    return 0;
}

console graph

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
#include <windows.h>
#include <GL/glut.h>
#include <cmath>
using namespace std;
 
//const double PI = 3.141592653589793238463;
//const float  PI_F = 3.14159265358979f;
 
// Window size
int width = 512;
int height = 512;
 
// Points
const int nPoinst = 20;
const int halfOfNPoints = nPoinst / 2;
float points[nPoinst] = {
    -500, 100,
    -400, -100,
    -300, 100,
    -200, -100,
    -100, 100,
    0, -100,
    100, 100,
    200, -100,
    300, 100,
    400, -100
};
 
void DrawAxis();
void DrawStrip();
void Enable2D(int width, int height);
 
void Init()
{
    Enable2D(width, height);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
 
void Draw()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
 
    DrawAxis();
    DrawStrip();
 
    glutSwapBuffers();
}
 
void DrawStrip()
{
    glBegin(GL_LINE_STRIP);
    glColor3f(0.0f, 0.0f, 0.0f);
    for (int i = 0, index = 0; i < halfOfNPoints; ++i, index += 2)
    {
        glVertex2f(points[index], points[index + 1]);
    }
    glEnd();
}
 
void DrawAxis()
{
    // Draw X axis
    glBegin(GL_LINES);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex2f(-width / 2.0f, 0.0f);
    glVertex2f(width / 2.0f, 0.0f);
    glEnd();
 
    // Draw Y axis
    glBegin(GL_LINES);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex2f(0.0f, -height / 2.0f);
    glVertex2f(0.0f, height / 2.0f);
    glEnd();
}
 
void Enable2D(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-width / 2.0f, width / 2.0f, -height / 2.0f, height / 2.0f, 0.0f, 1.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
 
 
int main(int argc, char** argv)
{
    // Initialize opengl (via glut)
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(width, height);
    glutCreateWindow("A");
    glutDisplayFunc(Draw);
    Init();
    glutMainLoop();
    return 0;
}
 

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

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

Смещение осей
Координата 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 = 0;
  • смещение по оси Y: OffsetY = maxY *height/MAX_Y

Значение 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).

 
Реализация на 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
132
133
134
135
136
137
138
139
140
141

#include <windows.h>
#include <math.h>
const int NUM = 70; // количество точек
LONG WINAPI WndProc(HWND, UINT, WPARAM, LPARAM);
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 = «My Class»;
  RegisterClass(&w);
  hwnd = CreateWindow(«My Class», «График функции»,
    WS_OVERLAPPEDWINDOW,
    500, 300, 500, 380, NULL, NULL,
    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, «y=sin(x)», 8);
    EndPaint(hwnd, &ps);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hwnd, Message, wparam, lparam);
  }
  return 0;
}

 
Результат выполнения
График функции sin(x)
Примечание: Для корректной сборки приложения используется многобайтовая кодировка.

Назад: Задачи и их решения

I have drawn the straight line using windows.h in code::blocks. I can’t explain it in details, but I can provide you a code and procedure to compile it in code::blocks.

  1. go to setting menu and select compiler and debugger.
  2. Click on linker tab and add a link library libgdi32.a which is at C:Program FilesCodeBlocksMinGWlib directory.

Now compile this program

#include <windows.h>

#include <cmath>

#define ROUND(a) ((int) (a + 0.5))

/* set window handle */

static HWND sHwnd;

static COLORREF redColor=RGB(255,0,0);

static COLORREF blueColor=RGB(0,0,255);

static COLORREF greenColor=RGB(0,255,0);


void SetWindowHandle(HWND hwnd){

sHwnd=hwnd;

}

/* SetPixel */

void setPixel(int x,int y,COLORREF& color=redColor){

if(sHwnd==NULL){

    MessageBox(NULL,"sHwnd was not initialized !","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

HDC hdc=GetDC(sHwnd);

SetPixel(hdc,x,y,color);

ReleaseDC(sHwnd,hdc);

return;

// NEVERREACH //

}


void drawLineDDA(int xa, int ya, int xb, int yb){

   int dx = xb - xa, dy = yb - ya, steps, k;

   float xIncrement, yIncrement, x = xa, y = ya;

   if(abs(dx) > abs(dy)) steps = abs(dx);

   else steps = abs(dy);

   xIncrement = dx / (float) steps;

   yIncrement = dy / (float) steps;

   setPixel(ROUND(x), ROUND(y));

   for(int k = 0; k < steps; k++){

    x += xIncrement;

    y += yIncrement;

    setPixel(x, y);

 }

}

/* Window Procedure WndProc */

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

 switch(message){

    case WM_PAINT:

        SetWindowHandle(hwnd);

        drawLineDDA(10, 20, 250, 300);

        break;

    case WM_CLOSE: // FAIL THROUGH to call DefWindowProc

        break;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    default:

    break; // FAIL to call DefWindowProc //

  }

 return DefWindowProc(hwnd,message,wParam,lParam);

}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int      iCmdShow){

static TCHAR szAppName[] = TEXT("Straight Line");

WNDCLASS wndclass;

wndclass.style         = CS_HREDRAW|CS_VREDRAW ;

wndclass.lpfnWndProc   = WndProc ;

wndclass.cbClsExtra    = 0 ;

wndclass.cbWndExtra    = 0 ;

wndclass.hInstance     = hInstance ;

wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName  = NULL ;

wndclass.lpszClassName = szAppName ;

// Register the window //

if(!RegisterClass(&wndclass)){

    MessageBox(NULL,"Registering the class failled","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

// CreateWindow //

HWND hwnd=CreateWindow(szAppName,"DDA - Programming Techniques",

            WS_OVERLAPPEDWINDOW,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             NULL,

             NULL,

             hInstance,

             NULL);

if(!hwnd){

    MessageBox(NULL,"Window Creation Failed!","Error",MB_OK);

    exit(0);

  }

  // ShowWindow and UpdateWindow //

  ShowWindow(hwnd,iCmdShow);

 UpdateWindow(hwnd);

 // Message Loop //

 MSG msg;

 while(GetMessage(&msg,NULL,0,0)){

    TranslateMessage(&msg);

    DispatchMessage(&msg);

 }

  /* return no error to the operating system */

  return 0;

}

In this program I have used DDA line drawing algorithm. Pixel drawing tasks is done by setPixel(ROUND(x), ROUND(y)) function.
This is windows programing which you can learn details here

I have drawn the straight line using windows.h in code::blocks. I can’t explain it in details, but I can provide you a code and procedure to compile it in code::blocks.

  1. go to setting menu and select compiler and debugger.
  2. Click on linker tab and add a link library libgdi32.a which is at C:Program FilesCodeBlocksMinGWlib directory.

Now compile this program

#include <windows.h>

#include <cmath>

#define ROUND(a) ((int) (a + 0.5))

/* set window handle */

static HWND sHwnd;

static COLORREF redColor=RGB(255,0,0);

static COLORREF blueColor=RGB(0,0,255);

static COLORREF greenColor=RGB(0,255,0);


void SetWindowHandle(HWND hwnd){

sHwnd=hwnd;

}

/* SetPixel */

void setPixel(int x,int y,COLORREF& color=redColor){

if(sHwnd==NULL){

    MessageBox(NULL,"sHwnd was not initialized !","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

HDC hdc=GetDC(sHwnd);

SetPixel(hdc,x,y,color);

ReleaseDC(sHwnd,hdc);

return;

// NEVERREACH //

}


void drawLineDDA(int xa, int ya, int xb, int yb){

   int dx = xb - xa, dy = yb - ya, steps, k;

   float xIncrement, yIncrement, x = xa, y = ya;

   if(abs(dx) > abs(dy)) steps = abs(dx);

   else steps = abs(dy);

   xIncrement = dx / (float) steps;

   yIncrement = dy / (float) steps;

   setPixel(ROUND(x), ROUND(y));

   for(int k = 0; k < steps; k++){

    x += xIncrement;

    y += yIncrement;

    setPixel(x, y);

 }

}

/* Window Procedure WndProc */

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){

 switch(message){

    case WM_PAINT:

        SetWindowHandle(hwnd);

        drawLineDDA(10, 20, 250, 300);

        break;

    case WM_CLOSE: // FAIL THROUGH to call DefWindowProc

        break;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    default:

    break; // FAIL to call DefWindowProc //

  }

 return DefWindowProc(hwnd,message,wParam,lParam);

}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int      iCmdShow){

static TCHAR szAppName[] = TEXT("Straight Line");

WNDCLASS wndclass;

wndclass.style         = CS_HREDRAW|CS_VREDRAW ;

wndclass.lpfnWndProc   = WndProc ;

wndclass.cbClsExtra    = 0 ;

wndclass.cbWndExtra    = 0 ;

wndclass.hInstance     = hInstance ;

wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName  = NULL ;

wndclass.lpszClassName = szAppName ;

// Register the window //

if(!RegisterClass(&wndclass)){

    MessageBox(NULL,"Registering the class failled","Error",MB_OK|MB_ICONERROR);

    exit(0);

}

// CreateWindow //

HWND hwnd=CreateWindow(szAppName,"DDA - Programming Techniques",

            WS_OVERLAPPEDWINDOW,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             CW_USEDEFAULT,

             NULL,

             NULL,

             hInstance,

             NULL);

if(!hwnd){

    MessageBox(NULL,"Window Creation Failed!","Error",MB_OK);

    exit(0);

  }

  // ShowWindow and UpdateWindow //

  ShowWindow(hwnd,iCmdShow);

 UpdateWindow(hwnd);

 // Message Loop //

 MSG msg;

 while(GetMessage(&msg,NULL,0,0)){

    TranslateMessage(&msg);

    DispatchMessage(&msg);

 }

  /* return no error to the operating system */

  return 0;

}

In this program I have used DDA line drawing algorithm. Pixel drawing tasks is done by setPixel(ROUND(x), ROUND(y)) function.
This is windows programing which you can learn details here

case MNUCLEAR:

Clear();

curspos = { 0, 1 };

// после очистки курсор

//в левый верхний угол консоли

break;

case MNUEXIT: int resp;

cout << «Вы уверены, что хотите выйти

из программы? (y/n)?»;

resp = getchar();

if (resp == ‘y’ || resp == ‘Y’)

{ gotoxy(0, 0); cls(1); exit(0); } getCursorPosition(); // запомнить положение курсора,

// если отменили выход

break;

}

fflush(stdin); //очистить буфер клавиатуры gotoxy(menu[sel].x, menu[sel].y); // курсор в

// текущий пункт меню

showCursor(false);

break;

case 120: // выход по клавише x case 88: // выход по клавише X case 27: // выход по клавише ESC

gotoxy(0, 0); cls(1);

exit(0); //завершение программы

}

}

}

}

//Текстовый курсор в точку x,y void gotoxy(int x, int y)

{

COORD cursorPos = { x, y }; SetConsoleCursorPosition(hStdOut, cursorPos); //SetConsoleCursorPosition(hStdOut, {x,y});

}

//запись текущего положения текстового курсора в глобальную

//переменную curspos

void getCursorPosition(void)

{

GetConsoleScreenBufferInfo(hStdOut, &csbInfo);

curspos = csbInfo.dwCursorPosition;// положение курсора

}

//очистка тестовой области консоли. Если it==0, то очистка со

//строки следующей за строкой меню, иначе очистка с левого

//верхнего угла консоли

31

void cls(int it)

{

int i;

string s(80, ‘ ‘);

SetConsoleTextAttribute(hStdOut, woкkWindowAttributes); if (it == 0) gotoxy(0, consolRect.Top + 1);

else gotoxy(0, consolRect.Top);

for (i = consolRect.Top; i<curspos.Y+1; i++) // очистка от

// первой строки до строки с курсором cout << s.c_str(); // залить фон строки меню

gotoxy(0, 0);

}

//выделить пункт меню с номером sel void itemMenu(int sel, bool activate)

{

WORD itemAttributes;

if (activate) itemAttributes = activeItemAttributes; else itemAttributes = inactiveItemAttributes; gotoxy(menu[sel].x, menu[sel].y); SetConsoleTextAttribute(hStdOut, itemAttributes); cout << menu[sel].str;

}

//скрыть/показать текстовый курсор в консоли

void showCursor(bool visible)

{

CONSOLE_CURSOR_INFO ccInfo; ccInfo.bVisible = visible; ccInfo.dwSize = 20; SetConsoleCursorInfo(hStdOut, &ccInfo);

}

Большую часть кода этого модуля составляет определение функции DrawMenu(). В ней «спрятана» вся функциональность меню. Функция рисует/печатает меню в верхней строке консольного окна, а затем запускает бесконечный цикл обработки событий нажатий клавиш. Комментарии в коде поясняют основные команды. Затем в модуле определяются некоторые вспомогательные функции.

Функция gotoxy(int x,int y) устанавливает курсор в заданную позицию.

Функция getCursorPosition(void) сохраняет текущее положение курсора в глобальную переменную curspos.

Функция cls(int it) выполняют очистку консоли (всей или без строки меню).

Функция itemMenu(int sel,bool activate) выделяет или снимает выделение пункта меню.

Функция showCursor(bool visible) делает невидимым или показывает текстовый курсор.

32

Замечание. Нашему меню далеко до функциональности, которую вы наблюдаете в меню программ Windows. Однако оно дает представление о том, как можно создать меню в текстовом режиме консоли. Вы можете использовать нашу программу как шаблон для ваших консольных программ. Все, что вам надо изменить находится в цикле обработки событий нажатий клавиш функции DrawMenu(), а также небольшом фрагменте кода, который еще раз приведен ниже.

// Изменяемые элементы меню

enum menuitems { MNUFILE, MNUDO, MNUCLEAR, MNUEXIT }; extern const int numMenu = 4; //количество пунктов меню

ITEM menu[numMenu] = {

//положение (x,y), заголовок,

// указатель на функцию

{ 1, 0, » Файл

«, File },

{11, 0, » Действие «, Do },

{21, 0, » Очистить «, Clear },

{ 31, 0, » Выход

«, Exit }

};

Изменения в программе, которые должен выполнить пользователь состоят в следующем:

изменить количество пунктов меню, задав значение константы numMenu;

Определить функции, которые будут вызываться при выборах пунктов меню (у нас функции File, Do, Clear, Exit определены в модуле menudemo.cpp) ;

добавить описание функций меню в модуль menudemo.h;

изменить количество пунктов и их заголовки в массиве menu[]; при

этом длины строк заголовков (в нашем примере » Файл «, » Действие «, » Очистить «, » Выход «) должна быть подобрана в соответствии с их первой координатой в этом массиве; у нас длина строк заголовков меню равна 10 символам);

изменить/добавить имена констант в перечислении menuitems; элементов в перечислении должно быть столько, сколько элементов меню;

в функции DrawMenu() в цикл обработки нажатий клавиш в раздел case KEY_ENTER внутри тела оператора switch(sel) нужно добавить код обработки выбора соответствующих пунктов меню. Например, если вы добавите в перечисление menuitems константу MYMENU, а функция, которая будет обрабатывать соответствующий ей пункт меню, будет называться mymenu(), то вы должны будете добавить

следующий код:

case MYMENU: mymenu();

getCursorPosition();

break;

33

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

Сделаем еще одно замечание. Большинство функций, которые определяют дескриптор окна, определяют или устанавливают его размер, атрибуты и т.д. могут не справиться со своей задачей. В этом случае они возвращают соответствующие значения, которые нужно (и правильно) проверять. Для простоты контроль ошибок в наших примерах не был реализован. Однако в реально функционирующем приложении такой контроль обязателен.

Например, чтение дескриптора консольного окна может выполняться следующим образом

hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout == INVALID_HANDLE_VALUE) {

MessageBox(NULL, L»Ошибка получения дескриптора», L»Ошибка», MB_OK | MB_ICONERROR);

return 1;

}

Здесь функция MessageBox выводит простое окно сообщений

Первый ее аргумент является идентификатором родительского окна или NULL, если такового нет. Второй аргумент является указателем на двухбайтовую строку, которая выводится в теле окна сообщения. Обычная строка превращается в массив символов типа wchar_t (каждый символ занимает в памяти два байта), если перед строкой указать префикс L. Окно MessageBox является диалоговым окном Windows и, поэтому, использует двухбайтовые коды символов в стандарте Unicode, в отличии от однобайтовых строк консоли. Третий аргумент является указателем на двухбайтовую строку, которая выводится в заголовке окна. Последний параметр типа UINT определяет какие в окне сообщений отображаются кнопки и иконки. Установка битов этого беззнакового целого числа говорит о том будет ли включен тот или иной элемент интерфейса окна сообщений. Чаще всего его задают комбинацией флагов. В приведенном выше фрагменте кода мы задали этот аргумент в виде комбинации двух флагов MB_OK | MB_ICONERROR. Первый из них говорит о необходимости отобразить кнопку OK, второй выводит иконку . А

например, флаг MB_YESNOCANCEL выводит в окне сообщений три кнопки:

.

Функция MessageBox возвращает целое значение, которое зависит от того, какая кнопка была нажата в окне сообщений.

34

Существует много других, не затронутых нами функций, которые предназначены для управления консольным окном. Познакомиться с ними вы можете по справочной системе в разделе «Console Functions».

1.2 Рисование в консольном окне

В этом параграфе мы покажем, как можно применять графические функции Windows для рисования в консольном окне. Однако помните, что графические возможности этого окна недостаточно функциональны и основным режимом работы консоли является текстовый. Тем не менее, все, что здесь будет изложено, без всяких изменений, но с некоторыми существенными улучшениями функциональности, используется в «оконных» приложениях

Windows.

Все графические функции C/C++ в Windows используют специальную структуру, которая называется контекстом окна. Мы здесь не будем описывать ее подробно. Вы должны знать, что она хранит текущие параметры области, в которой мы рисуем: цвет линий, их толщину и стиль (пунктир, сплошной,…), цвет заливки областей, размеры окна и т.д. В программах на C/C++ контекст окна хранится в переменной типа HDC, которая должна быть связана с окном, в котором происходит рисование. Простейшая команда, которая создает такую переменную, имеет вид

HDC hdc = GetDC(GetConsoleWindow());

Сейчас вы можете не задумываться о ее смысле – просто включайте эту строку в начало функции main(), а возвращенный ее дескриптор используйте во всех графических функциях. Любая функция WINDOWS, которая что-то рисует, первым параметром будет принимать переменную hdc. Перед завершением программы дескриптор контекста консольного окна рекомендуется освободить командой

ReleaseDC(NULL, hdc);

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

HWND hwnd = GetConsoleWindow();

Если он использовался в программе, то его идентификатор hwnd должен был бы быть передан первым аргументом в функцию ReleaseDC(hwnd,hdc).

В случае ошибки функции GetConsoleWindow() и GetDC() возвращают специальные значения, которые рекомендуется проверять, например, следующим образом:

HWND hwnd = GetConsoleWindow(); HDC hdc;

if (hwnd!=NULL)

{

hdc=GetWindowDC(hwnd);

if(hdc==0) cout<<«Error DC Window»<<endl;

}

35

else

cout << «Error Find Window» << endl;

Для упрощения кода в примерах этого параграфа мы не будем выполнять множество проверок значений, возвращаемых функциями, которые получают или создают различные объекты Windows (или их идентификаторы). Однако в реально функционирующих приложениях такой контроль обязателен.

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

Пример 1a. В этом примере мы рисуем прямоугольник в консольном окне.

#include <windows.h>

void main()

{

HDC hdc = GetDC(GetConsoleWindow()); Rectangle(hdc, 100, 60, 180, 160); system(«pause»);

}

Здесь переменная hdc, содержащая дескриптор контекста консольного окна, передается первым аргументом функции Rectangle(hdc,…), которая рисует прямоугольник. Другие аргументы этой функции – координаты левого верхнего и правого нижнего углов xleft , yleft , xright , yright .

Начало координат находится в левом верхнем углу рабочей области окна. Горизонтальная ось X направлена вправо, вертикальная Y – вниз. По умолчанию единицами измерения являются пиксели.

Обратите внимание на поведение рисунка после сворачивания консольного окна, перекрытия его другим окном, изменении размера или прокручивании его внутренней области. Графика исчезает! Этого недостатка обычно нет у стандартных окон Windows потому, что они «умеют» автоматически перерисовывать свою рабочую область после событий сворачивания, перекрытия или изменения размера. В учебных консольных программах перерисовку можно выполнять вручную. Для этого любое рисование следует помещать внутрь создаваемой вами функции draw(.), и вызывать ее повторно в случае, если графика окна исчезла.

Пример 1b. Более гибкая версия консольной программы с графикой.

#include <windows.h> #include <iostream> #include <conio.h> using namespace std;

void draw(HWND hwnd, HDC hdc); void setConsoleSize();

36

void main()

{

SetConsoleTitle(L»Simple Rectangle Drawing»); HWND hwnd = GetConsoleWindow();

HDC hdc = GetDC(hwnd);

setConsoleSize();

// задание размеров консоли

Sleep(100);

draw(hwnd, hdc);

// функция рисования в консоли

int iKey = 1;

while (iKey != 27) {

// Задержка и выход по клавише ESC

if (_kbhit()) {

iKey = _getch();

switch (iKey)

{

case 112: case 80: case 167: case 135:

draw(hwnd, hdc);

// перерисовка консоли по клавише ‘p’

break;

}

}

}

ReleaseDC(hwnd, hdc);

//освобождаем дескрипторы консоли

}

// Функция рисования. Помещайте сюда всю графику void draw(HWND hwnd, HDC hdc)

{

Rectangle(hdc, 100, 60, 180, 160); // Рисуем прямоугольник

}

void setConsoleSize() // Задание размеров окна консоли

{

const int colConsole = 80; const int rowConsole = 30;

HANDLE hNdl = GetStdHandle(STD_OUTPUT_HANDLE); SMALL_RECT windowSize ={0,0,colConsole-1,rowConsole-1}; SetConsoleWindowInfo(hNdl, TRUE, &windowSize);

COORD bufferSize ={colConsole, rowConsole }; // размеры буфера SetConsoleScreenBufferSize(hNdl, bufferSize);

}

Во второй версии программы мы создали функцию setConsoleSize(.), которая задает размеры консольного окна без полос прокрутки (пока мы не уменьшили размеры окна). Мы также вынесли рисование в отдельную функцию draw(.), которую можем вызывать по нажатию клавиши „p‟, например для перерисовки графики, если она пропала после перекрытия консоли другим окном. Перед завершением программы мы также освободили дескрипторы консоли.

Как вы заметили, графика в консоли видна тогда, когда рисование выпоняется в видимое окно. После сворачивания или перекрытия окна консоли графика исчезает. На самом деле приложения Windows получают сообщение от ОС о том, что они должны перерисовать свое окно (или его часть) и

37

программисты помещают вызов графических функций в обработчик этого сообщения (специальную функцию). В программе, написанной для консоли, мы не можем перехватить и обработать такое сообщение. Но перерисовка консоли после восстановления видимости ее окна все равно происходит. Это приводит к рисованию «пустоты», т.е. к очистке окна. Поэтому нам приходится «выкручиваться». После сворачивания или перекрытия окна консоли рисование нужно повторить. Для этого в предыдущей программе мы создали функцию draw(.), которую могли вызвать клавишей „p‟.

Обратите также внимание на функцию Sleep(.), которую мы вызываем перед вызовом функции draw(.). Она приостанавливает выполнение потока инструкций программы на заданное количество милисекунд. На современных компьтерах, если в коде предыдущей программы не использовать функцию Sleep(.), функция draw(.) успевает выполниться еще до того, как консоль станет видимой на экране. После появления консоли на экране, окно получает сообщение о необходимости перерисовки, и в результате происходит очистка окна. Вставка функции Sleep() перед вызовом функции draw приводит к тому, что окно консоли станет видимым раньше того, как мы что – то нарисуем. Время задержки будет зависеть от быстродействия компьютера. Возможно, вставка функции Sleep()вам не понадобится.

В консольном приложении у нас есть возможность обрабатывать сообщение получения окном фокуса. Это сообщение помещается в буфер событий, который заполняется функцией ReadConsoleInput. Мы использовали эту функцию в примере 8 предыдущего параграфа. Она помещает в буфер событий информацию о мыши, клавиатуре, а также о получении окном фокуса. Если, например, буфер/массив событий наывается eventBuffer, то проверку того, что консоль получила фокус, можно выполнить следующим образом

if (eventBuffer[i].EventType == FOCUS_EVENT) { Sleep(300);

draw(hwnd, hdc);

}

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

Пример 1c. Пример консольной программы с перерисовкой графики при получении окном фокуса.

#include <windows.h>

void draw(HWND hwnd, HDC hdc); void setConsoleSize();

38

void main()

{

HWND hwnd = GetConsoleWindow(); HDC hdc = GetDC(hwnd);

HANDLE rHnd = GetStdHandle(STD_INPUT_HANDLE); SetConsoleTitle(L»Simple Rectangle Drawing»);

setConsoleSize();

Sleep(100);

//

задержка

draw(hwnd, hdc);

//

рисование любого множества фигур

DWORD numEvents = 0; // Количество непрочитанных сообщений DWORD numEventsRead = 0; // Количество прочитанных сообщений bool isRunning = true; // флаг продолжения работы

//Ели isRunning=false, то программа завершается while (isRunning) {

// Определить количество событий консоли

GetNumberOfConsoleInputEvents(rHnd, &numEvents); if (numEvents != 0) {

//выделение памяти для хранения данных о событиях

INPUT_RECORD *eventBuffer = new INPUT_RECORD[numEvents];

//Извлечение данных во временный буфер событий eventBuffer[] ReadConsoleInput(rHnd, eventBuffer, numEvents,

&numEventsRead);

// Цикл по всем извлеченным событиям

for (DWORD i = 0; i < numEventsRead; ++i) { if (eventBuffer[i].EventType == KEY_EVENT) {

if(eventBuffer[i].Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)

isRunning=false; // выход, если нажата клавиша ESC else if(eventBuffer[i].Event.KeyEvent.uChar.AsciiChar==

‘d’)

draw(hwnd, hdc);

// перерисовка по клавише ‘d’

}

if (eventBuffer[i].EventType == FOCUS_EVENT) {

Sleep(300);

draw(hwnd, hdc);

// перерисовка при получении фокуса

}

else if (eventBuffer[i].EventType == MOUSE_EVENT) {

// обработка событий мыши

}

}

delete[] eventBuffer;

}

}

ReleaseDC(hwnd, hdc); //освобождаем дескрипторы

}

Здесь мы используем функцию setConsoleSize(), которую создали в примере 1b, и код которой остался без изменений, а также функцию draw(.), в которую вы можете помещать любые функции рисования.

Изменение размеров окна консоли не приводит к событию FOCUS_EVENT, но вызывает очистку окна. Поэтому мы оставили команду принудительной перерисовки – вызов функции draw при нажатии клавиши „d‟.

39

В программах, которые приведены далее в этом параграфе, для простоты не будет реализован контроль ошибок, не будут освобождаться ресурсы, и графические функции не будут собираться в отдельную функцию (например, draw). Однако в реально функционирующих приложениях соответствующие фрагменты кода обязательны.

Линия может быть жирной и тонкой, прерывистой и штрих – пунктирной, иметь определенный цвет. Это все вместе называется стилем линии (или просто пером). Текущее перо является частью структуры контекста окна. Чтобы линию нарисовать требуемым пером, вначале вы должны создать объект типа HPEN

HPEN Pen = CreatePen(PS_SOLID, 3, RGB(255,0,0));

Первый аргумент функции CreatePen определяет будет ли линия сплошной (PS_SOLID), пунктирной, штрих – пунктирной и т.д. Можно использовать значения PS_DOT, PS_DASH, PS_DASHDOT и некоторые другие. Второй аргумент определяет толщину линии, третий – задает цвет. Аргументы макроса RGB являются целыми числами со значениями от 0 до 255 и задают цвет, составленный из оттенков/яркостей трех базовых цветов: красного (Red), зеленого (Green) и синего (Blue). Нулевое значение исключает соответствующий базовый цвет, максимальное допустимое значение яркости базовых цветов 255.

По умолчанию установлено черное сплошное перо толщиной в 1 пиксель. После создания пера (HPEN) его нужно загрузить в контекст (HDC)

функцией SelectObject(HDC,HPEN). После этого все линии будут рисоваться созданным пером до тех пор, пока в контекст не будет загружено новое перо.

Есть еще один момент. В контексте хранится положение так называемого текущего указателя, который содержит координаты точки от которой следующая графическая функция начинает рисовать свою фигуру. При запуске программы этот указатель устанавливается в точку (0,0). Его местоположение на экране невидимо, он означает лишь позицию в окне для некоторых функций. Не все графические функции используют этот указатель. Функция

MoveToEx((HDC,X,Y,NULL);

переносит текущую позицию указателя в точку (X,Y) и не запоминает старую позицию (если последний аргумент равен NULL).

В следующем примере мы используем функцию

LineTo(HDC,X,Y)

которая рисует отрезок прямой от текущего положения указателя до точки (X,Y) и перемещает указатель в эту точку.

Пример 2.

#include <windows.h> void main()

{

// получаем дескриптор контекста консольного окна

HDC hdc = GetDC(GetConsoleWindow());

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

    23.02.2015231.42 Кб4DS.doc

  • #

Сразу предупрежу – материал очень сырой и некоторые моменты я пропущу. Но это скорее всего все я дополню позднее. Во всяком случае надеюсь, что все получится.

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

 Я выбрал простой график y=x*x Короткая формула, всем известная кривая (всем кто хоть чуть-чуть учился в школе)

 Код C++ Рисование графика
============
#include <graphics.h> // включить описание графических функций С/С++ Borland’a
#include <conio.h>     
// описание функций ввода-вывода с консоли (для getch())

int main()
{
float x, y;
/* Автоматическое определение графических параметров */
int gdriver = DETECT, gmode;
initgraph(&gdriver, &gmode, “”);    
// Инициализация графического режима

x=-10; //Инициализируем x
moveto(x,x*x);
//Устанавливаем курсор

do
{
y=x*x;
//Наша формула для построения графика
lineto(x*50+getmaxx()/2, getmaxy()/2
-(y*20));     // ..откуда рисуем график
x=x+0.02;
} while(x<10);
getch();               
  // ожидание нажатия пользователем любой клавиши
closegraph();       
// выход из графического режима
return 0;
}

============

Что ж. Сразу скажу – Я не знаю почему x увеличивается на дробное число. При моих попытках увеличивать его на единицу график получался очень даже не гладким и это было плохо, поэтому оставил так как отыскал в просторах интернета. Чтобы начать рисовать линию из какой-то точки имеет смысл определить первоначальную точку, для чего была использована функция moveto Сам график строится с помощью циклического вычисления, ведь для каждого значения x нужно получить соответствующий ему y, значит, чтобы не писать все эти выражения вручную нужно использовать цикл.

 Внутри цикла вызывается функция lineto, внутри которой написаны такие параметры, которые могут сбить с толку бедного новичка, но пугаться не стоит. lineto чертит линию от текущей позиции до, но не включая в нее, указанной точки. Другими словами при вычислении значения y мы будем получать некоторые точки, а чтобы получить график, нужно эти точки соединять линиями.
 x*50+getmaxx()/2 Обозначает, что первоначальная точка x смещена к центру экрана по оси x. Цифра 50 здесь нужна только для того чтобы расширить рисуемый график. Нетрудно попробовать её изменить или убрать, чтобы увидеть эффект. Когда я пытался убрать эту 50 и прибавлять к x единицу вместо 0.02 график рисовался ужасно, хотя если подумать, то то что написано здесь или то что хотел написать я сводится к тому что x прибавляется на единицу, но что-то вот в этом есть чего я понять пока что увы не могу. Точнее понимаю, но понимаю как-то туманно, не полностью и сложно для разъяснений

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

Published
August 14, 2012

Понравилась статья? Поделить с друзьями:
  • Windows graphics programming win32 gdi and directdraw
  • Windows install programs from command line
  • Windows git скачать через командную строку
  • Windows insider как стать участником windows 11
  • Windows insider windows 11 download for windows