Rappresentazione Grafica di Funzioni Matematiche con MFC

Prendendo come spunto la rappresentazione grafica di alcune funzioni matematiche, voglio spiegare il procedimento per disegnare con la libreria MFC. Nella programmazione Windows, la tela sulla quale disegnare si chiama DC ( Device Context ) ed in MFC abbiamo una classe che incapsula questo, si chiama CDC. Ci sono anche delle classi derivate da CDC che sono specifiche per alcune parti della finestra: CPaintDC per disegnare nell’evento WM_PAINT, CClientDC per disegnare nella client area delle finestre, CWindowDC per disegnare nell’intera parte della finestra.
La prima operazione da fare per disegnare in una finestra è ottenere quindi il DC, su questo poi è possibile selezionare matite, pennelli, font, bitmap che saranno utilizzate per tratteggiare linee, rettangoli ed altro. In questo tutorial vedremo come utilizzare gli oggetti base della libreria MFC, quindi soltanto le linee, attraverso queste parti basilari del disegno è infatti possibile rappresentare delle funzioni, come in questo esempio:

// File di intestazione Funzioni.h

class CMyApp : public CWinApp
{
public:
   virtual BOOL InitInstance();
};

class CMainWnd : public CFrameWnd
{
public:
   CMainWnd();

   DECLARE_MESSAGE_MAP();
   afx_msg void OnPaint();
};

// Disegnare funzioni con MFC

#include <afxwin.h>
#include "Funzioni.h"
#include <math.h>

CMainWnd::CMainWnd()
{
   Create(NULL, "Rappresentare Funzioni",
      WS_OVERLAPPEDWINDOW);
}

BOOL CMyApp::InitInstance()
{
   m_pMainWnd = new CMainWnd();
   m_pMainWnd->ShowWindow(m_nCmdShow);
   m_pMainWnd->UpdateWindow();

   return TRUE;
}

CMyApp theApp;

BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
   ON_WM_PAINT()
END_MESSAGE_MAP()

void CMainWnd::OnPaint()
{
   // Ecco la prima funzione ( y = x + 10 ) da disegnare
   // Ecco la seconda funzione ( y = 4x2 + 6x - 8 ) da disegnare
   // Ecco la terza funzione ( y = 8x3 - 6x2 + 4x + 2 ) da disegnare

   // Otteniamo la tela per disegnare
   CPaintDC dc(this);

   // Otteniamo le dimensioni della finestra e
   // calcoliamo il centro della finestra
   CRect rcWin; CPoint ptCenter;
   GetClientRect(&rcWin);
   ptCenter = rcWin.CenterPoint();

   // Creiamo ed applichiamo una matita nera di spessore 1
   CPen pen(PS_SOLID, 1, RGB(0, 0, 0));
   CPen* pOldPen = dc.SelectObject(&pen);
   // Creiamo ed applichiamo un pennello nero
   CBrush brush(RGB(0, 0, 0));
   CBrush* pOldBrush = dc.SelectObject(&brush);

   // coloriamo tutta la finestra di un leggero grigio
   dc.FillSolidRect(&rcWin, RGB(200, 200, 200));

   // Disegniamo l'ascissa con la freccia
   dc.MoveTo(5, ptCenter.y);
   dc.LineTo(rcWin.right - 20, ptCenter.y);
   dc.LineTo(rcWin.right - 20, ptCenter.y - 5);
   dc.LineTo(rcWin.right - 5, ptCenter.y);
   dc.LineTo(rcWin.right - 20, ptCenter.y + 5);
   dc.LineTo(rcWin.right - 20, ptCenter.y);
   dc.FloodFill(rcWin.right - 10, ptCenter.y, RGB(0, 0, 0));

   // Disegniamo l'ordinata con la freccia
   dc.MoveTo(ptCenter.x, rcWin.bottom - 5);
   dc.LineTo(ptCenter.x, rcWin.top + 20);
   dc.LineTo(ptCenter.x - 5, rcWin.top + 20);
   dc.LineTo(ptCenter.x, rcWin.top + 5);
   dc.LineTo(ptCenter.x + 5, rcWin.top + 20);
   dc.LineTo(ptCenter.x, rcWin.top + 20);
   dc.FloodFill(ptCenter.x, rcWin.top + 10, RGB(0, 0, 0));

   // Creiamo ed applichiamo una matita blu di spessore 1
   dc.SelectObject(pOldPen);
   pen.DeleteObject();
   pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
   dc.SelectObject(&pen);

   // Ora possiamo fare i calcoli e disegnare la prima funzione
   for (int k = rcWin.left + 5; k <= rcWin.right - 20; k++)
   {
       int x = k;
       x -= ptCenter.x;

       int y = -(x + 10);
       y += ptCenter.y;

       if (k == rcWin.left + 5)
          dc.MoveTo(k, y);

       dc.LineTo(k, y);
    }

   // Creiamo ed applichiamo una matita rossa di spessore 1
   dc.SelectObject(pOldPen);
   pen.DeleteObject();
   pen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
   dc.SelectObject(&pen);

   // Ora possiamo fare i calcoli e
   // disegnare la seconda funzione
   for (int k = rcWin.left + 5; k <= rcWin.right - 20; k++)
   {
      int x = k;
      x -= ptCenter.x;

      int y = -((int)(4 * pow(x, 2.0))
            + 6 * x - 8);
      y += ptCenter.y;

      if (k == rcWin.left + 5)
	dc.MoveTo(k, y);

      dc.LineTo(k, y);
   }

   // Creiamo ed applichiamo una matita verde di spessore 1
   dc.SelectObject(pOldPen);
   pen.DeleteObject();
   pen.CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
   dc.SelectObject(&pen);

   // Ora possiamo fare i calcoli e disegnare la terza funzione
   for (int k = rcWin.left + 5; k <= rcWin.right - 20; k++)
   {
       int x = k;
       x -= ptCenter.x;

       int y = -((int)(8 * pow(x, 3.0) +
         6 * pow(x, 2.0)) + 4 * x + 2);
       y += ptCenter.y;

       if (k == rcWin.left + 5)
	dc.MoveTo(k, y);

       dc.LineTo(k, y);
   }

   dc.SelectObject(pOldPen);
   pen.DeleteObject();
   dc.SelectObject(pOldBrush);
   brush.DeleteObject();
}

Questo esempio è solo per scopi didattici e non ha assolutamente valenza pratica, serve solo a farvi vedere come è possibile rappresentare delle funzioni in linguaggio C++ con MFC. Come potete notare prima si creano gli assi cartesiani e poi si disegnano dei grafici utilizzando soltanto le linee. Alla fine del disegno conviene liberare le risorse impegnate distruggendo tutto quello che si è creato. Il bello di utilizzare il device context è che esso rappresenta una qualsiasi periferica di output, quindi video e stampante per esempio. Come potete osservare dall’esempio le coordinate vanno dall’angolo superiore sinistro fino in basso, un tipo di mappatura di default testuale. In un prossimo appuntamento vedremo tutti i tipi di mappatura del DC in modo da poter utilizzare quello che il nostro disegno richiede, inoltre vedremo come spostare l’origine degli assi dove vogliamo all’interno della tela.

Informazioni su Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.
Questa voce è stata pubblicata in VC/C++. Contrassegna il permalink.