Second Opinion
Folosind serviciul second opinion ne puteți trimite RMN-uri, CT -uri, angiografii, fișiere .pdf, documente medicale. Astfel vă vom putea da o opinie neurochirurgicală, fără ca aceasta să poată înlocui un consult de specialitate. Răspunsurile vor fi date prin e-mail în cel mai scurt timp posibil (de obicei în mai putin de 24 de ore, dar nu mai mult de 48 de ore). Second opinion – Neurohope este un serviciu gratuit. www.neurohope.ro |
Reprezentarea graficului unei funcții în C
Last Updated: May 03 2014 22:45, Started by
dani.user
, Feb 22 2014 13:44
·
0
#1
Posted 22 February 2014 - 13:44
Cum desenez graficul unei funcții în C?
O intrebare pe care mulți ne-am pus-o la un moment dat, dar la care nu prea am găsit răspuns. Unii se gândesc la celebrul graphics.h al Borland, însă ... ce naiba ... suntem in 2014, trebuie să existe ceva mai modern. Și chiar există ceva mai modern. În ziua de azi, sistemul de operare este cel care controlează interacțiunile cu perificericele (inclusiv partea video) așa că va trebui să luam legătura cu el pentru a obține ceea ce dorim. Acest lucru este chiar benefic, fiindcă altfel ar fi trebuit să scriem cod diferit funcție de ce placă video are utilizatorul, lucru care chiar nu ne interesează. Deoarece majoritatea celor ce vor citi acest tutorial folosesc Windows, voi exemplifica folosind particularitățiile acestui sistem de operare. Logica rămâne aceeași indiferent de tehnologia folosită, ce diferă fiind funcțiile efective de desenare a liniilor, textului, etc. Untitled.png 21.09K 218 downloads Desenarea sub Windows API-ul tradițional de desenare sub Windows, GDI, există de când lumea. Întretimp au mai apărut tehnologii mai moderne pentru lucrul în 2D: GDI+, Direct2D; însă pentru acest exemplu sunt suficiente facilitățiile oferite de GDI. Astfel (cu mici ajustări) aplicația ar putea rula și pe Windows 95. Citirea celuilalt tutorial pe această temă, Visual C++ 2010 Express pentru tăţi, este recomandată. Crearea unei ferestre Mediul Windows, așa cum îi indică și numele, este compus din ferestre. Astfel, primul pas este să creăm și noi o fereastră. Acest lucru nu este chiar ușor la început, însă, din fericire, majoritatea IDE-urilor au șabloane minimale pentru acest lucru. Așa arată șablonul specific din Visual Studio. // Global Variables: HINSTANCE hInst; // current instance TCHAR szTitle[] = _T("Grafice de functii"); // The title bar text TCHAR szWindowClass[] = _T("FUNCTIONPLOTTER"); // the main window class name // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); MSG msg; MyRegisterClass(hInstance); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX 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(hInstance, MAKEINTRESOURCE(IDI_FUNCTIONPLOTTER)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_3DSHADOW); wcex.lpszMenuName = 0; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // Store instance handle in our global variable hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); RefreshPlot(hWnd, hdc); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } Acest cod face următoarele:
Când/cum/unde desenez? În exemplu vom desena de fiecare dată când sistemul ne anunță că o redesenare este necesară (mesajul WM_PAINT). Există și metode mai eficiente, însă acesta este un mod foarte simplu de a obține un grafic redimensionabil. Pasul 1 - Informații Ce avem la dispoziție? Avem la dispoziție o fereastră (un dreptunghi) de o anumită dimensiune. Dacă fereastra e maximizată, dimensiunea ei va fi apropiată de rezoluția ecranului (ex. 1920x1080). În această fereastră vom desena graficul, lăsând o ușoară spațiere față de marginea ferestrei. Vom putea controla fiecare pixel din această fereastră. Ce vrem să afisăm? Trebuie să ne hotărâm ce parte a graficului dorim să afisăm. În continuare vom fixa afișa x de la -6 la 6, iar y de la -1.5 la 1.5. Fiind o aplicație cu interfață grafică, acești parametrii ar fi fain de introdus de către utilizator, dar aceasta rămâne temă de casă. O alta informație importantă este frecvența marcajelor pe axe. Am ales să pun un marcaj din 1 în 1 pe abscisă și din 0.5 în 0.5 pe ordonată. Toți acești parametrii globali ai graficului sunt stocați într-o structură. typedef struct { RECT plotArea; double xFrom; double xTo; double xGridSpacing; TCHAR *xMarkerFormat; double yFrom; double yTo; double yGridSpacing; TCHAR *yMarkerFormat; } PlotParameters; De ce altă informație avem nevoie? Cea mai importantă informație e ... funcția al cărei grafic vrem să-l desenăm. Deoarece codul vrem să funcționeze cu orice funcție, va trebui ca funcția de desenare sa primească un pointer la funcția dorită. Am definit astfel un sinonim pentru acest de tip funcție, o funcție ce acceptă un parametru de tip double, și returnează tot un double: typedef double(*FunctionType)(double); Apoi, vrem să putem afișa mai multe funcții în același timp. Astfel, pentru a le diferenția, voi asocia fiecărei funcții o culoare, și un scurt text de afișat în legendă. Deasemenea vom putea specifica stilul graficului (normal, punctat, etc). typedef struct { FunctionType function; COLORREF color; int style; TCHAR *legend; } PlotAction; Informații auxiliare Pe lângă toate acest informații, mai sunt o sumedenie de informații mai puțin relevante în primă fază: culoarea marginii, fontul folosit, culoarea de fundal, etc. Pentru simplitate, aceastea au fost definite drept constante în macro-uri. #define PlotAreaBorderColor RGB(0, 0, 0) #define PlotAreaBackgroundColor RGB(255, 255, 255) #define PlotAreaBorderStyle PS_DASH #define AxisColor RGB(0, 0, 200) #define AxisStyle PS_SOLID #define AxisWidth 2 #define AxisMarkerLength 20 #define AxisMarkerFont _T("Comic Sans MS") #define AxisMarkerFontSize 10 #define AxisArrowLength 15 #define AxisArrowAngle 20 #define GridColor RGB(200, 200, 200) #define GridStyle PS_DOT #define GridWidth 1 #define LegendFont _T("Comic Sans MS") #define LegendFontSize 12 #define LegendTextColor RGB(0, 0, 0) #define LegendBackgroundColor RGB(240, 240, 240) #define LegendStyle PS_DASH #define LegendItemWidth 30 #define LegendWidthSpacing 10 #define LegendHeightSpacing 10 Pasul 2 - Transpunerea coordonatelor Cele mai multe calcule vor fi legate de transpunerea unui punct din coordonatele matematice, în coordonatele ecranului. Să luăm un exemplu: Într-un dreptunghi de 500x400 pixeli vom afișa valorile cuprinse între -5 si 5 pe abscisă și -1.5 si 1.5 pe ordonată. Știm deasemenea că acei 500x400 au originea în colțul din stânga sus (nu în centru, cum suntem obișnuiți de la matematică). La ce coordonate va trebui astfel afișat punctul ... să zicem ... (2, 1). În intervalul -5..5, valoarea 2 se află mai in dreapta (spre 5), mai exact la (2 - (-5)) / (5 - (-5)) * 100% = 7 / 10 * 100% = 70%. Aplicând acesti 70% pe lungimea in pixeli, 500, obținem: 70% * 500 = 350. În intervalul -1.5..1.5, valoarea 1 se află mai la (1 - (-1.5)) / (1.5 - (-1.5)) * 100% = 2.5 / 3 * 100% = 83.33%. Aplicând acesti 83.33% pe lațimea in pixeli, 400, obținem: 83.33% * 400 ~= 333. Trebuie însă să ținem cont că valoarea 0 este sus și valoarea 400 jos, astfel valoarea finală va fi 400 - 333 = 67. (valoarea 1 se află în jumătatea de sus, mai spre 1.5 decât spre -1.5) Astfel punctul (2, 1) va fi afișat pe ecran la (350, 67). Pasul 3 - RefreshPlot Atunci când sistemul cere o redesenare a conținutului, apelăm funcția RefreshPlot. Aceasta arată în felul următor: void RefreshPlot(HWND hWnd, HDC hdc) { RECT plotArea; int windowWidth; int windowHeight; const int topMargin = 10; const int leftMargin = 10; const int rightMargin = 10; const int bottomMargin = 10; derivativeFrom = f; PlotAction toPlot[] = { { f, RGB(200, 0, 0), PS_SOLID, _T("f(x) = 0.4x^3 + 0.2x^2 - x + 0.6") }, { sin, RGB(0, 200, 0), PS_DASH, _T("sin(x)") }, { derivative, RGB(150, 150, 0), PS_SOLID, _T("f'(x) = 1.2x^2 + 0.4x - 1") }, { NULL, 0, 0, NULL } }; GetClientRect(hWnd, &plotArea); windowWidth = plotArea.right - plotArea.left; windowHeight = plotArea.bottom - plotArea.top; plotArea.top = topMargin; plotArea.left = leftMargin; plotArea.right = windowWidth - rightMargin; plotArea.bottom = windowHeight - bottomMargin; PlotParameters parameters; parameters.plotArea = plotArea; parameters.xFrom = -6; parameters.xTo = 6; parameters.xGridSpacing = 1; parameters.xMarkerFormat = _T("%0.0f"); parameters.yFrom = -1.5; parameters.yTo = 1.5; parameters.yGridSpacing = 0.5; parameters.yMarkerFormat = _T("%0.1f"); Plot(hdc, toPlot, ¶meters); }
Pasul 4 - Plot Funcția de reprezentare mai sus apelată, Plot, apelează la rându-i mai departe alte funcții, după ce determină câte funcții are de reprezentat. void Plot(HDC hdc, PlotAction actions[], const PlotParameters* parameters) { int i; DrawPlottingArea(hdc, parameters); for (i = 0; actions[i].function != NULL; i++) { DrawFunction(hdc, &actions[i], parameters); } DrawLegend(hdc, actions, parameters); } Pasul 5 - DrawPlottingArea DrawPlottingArea se ocupă de crearea unui chenar în care vor fi afișate graficele, de desenarea axelor și a valorilor de pe axe, de desenarea unui grid pentru a repera mai ușor valorile. Desenarea liniilor se face cu ajutorul unui "pen" (identificat printr-un HPEN).
În cazul desenării axelor de coordonate se observă o aplicare a geometriei pentru a determina coordonatele vărfurile săgețiilor. Exempul de mai jos desenează o axă doar dacă valorile sunt potrivite. De exemplu, ordonata nu apare dacă valorile afișate a lui x sunt doar pozitive sau negative. void DrawPlottingArea(HDC hdc, const PlotParameters* parameters) { int xGridSpacing, yGridSpacing; DrawPlottingRectangle(hdc, parameters); xGridSpacing = (int)round(parameters->xGridSpacing / (parameters->xTo - parameters->xFrom) * (parameters->plotArea.right - parameters->plotArea.left)); yGridSpacing = (int)round(parameters->yGridSpacing / (parameters->yTo - parameters->yFrom) * (parameters->plotArea.bottom - parameters->plotArea.top)); DrawGrid(hdc, parameters, xGridSpacing, yGridSpacing); DrawAxes(hdc, parameters, xGridSpacing, yGridSpacing); } void DrawPlottingRectangle(HDC hdc, const PlotParameters* parameters) { HPEN previousPen, pen = CreatePen(PS_DASH, 1, PlotAreaBorderColor); HBRUSH previousBrush, brush = CreateSolidBrush(PlotAreaBackgroundColor); previousPen = (HPEN)SelectObject(hdc, pen); previousBrush = (HBRUSH)SelectObject(hdc, brush); Rectangle(hdc, parameters->plotArea.left, parameters->plotArea.top, parameters->plotArea.right, parameters->plotArea.bottom); SelectObject(hdc, previousPen); SelectObject(hdc, previousBrush); } void DrawGrid(HDC hdc, const PlotParameters* parameters, int xGridSpacing, int yGridSpacing) { HPEN previousPen, pen = CreatePen(GridStyle, GridWidth, GridColor); int position; previousPen = (HPEN)SelectObject(hdc, pen); position = parameters->plotArea.left; while (position + xGridSpacing < (parameters->plotArea.right - xGridSpacing * 3 / 4)) { position += xGridSpacing; MoveToEx(hdc, position, parameters->plotArea.top, NULL); LineTo(hdc, position, parameters->plotArea.bottom); } position = parameters->plotArea.top; while (position + yGridSpacing < (parameters->plotArea.bottom - yGridSpacing * 3 / 4)) { position += yGridSpacing; MoveToEx(hdc, parameters->plotArea.left, position, NULL); LineTo(hdc, parameters->plotArea.right, position); } SelectObject(hdc, previousPen); } void DrawAxes(HDC hdc, const PlotParameters* parameters, int xGridSpacing, int yGridSpacing) { HPEN previousPen, pen = CreatePen(AxisStyle, AxisWidth, AxisColor); int xAxisY = 0, yAxisX = 0, arrowX, arrowY, markerPosition; previousPen = (HPEN)SelectObject(hdc, pen); if (parameters->yFrom < 0 && parameters->yTo > 0) { // the X axis is visible xAxisY = (int)round(parameters->plotArea.top + parameters->yTo / (parameters->yTo - parameters->yFrom) * (double)(parameters->plotArea.bottom - parameters->plotArea.top)); MoveToEx(hdc, parameters->plotArea.left, xAxisY, NULL); LineTo(hdc, parameters->plotArea.right, xAxisY); } if (parameters->xFrom < 0 && parameters->xTo > 0) { // the Y axis is visible yAxisX = (int)round(parameters->plotArea.right - parameters->xTo / (parameters->xTo - parameters->xFrom) * (double)(parameters->plotArea.right - parameters->plotArea.left)); MoveToEx(hdc, yAxisX, parameters->plotArea.top, NULL); LineTo(hdc, yAxisX, parameters->plotArea.bottom); } if (xAxisY > 0) { markerPosition = parameters->plotArea.left; while (markerPosition + xGridSpacing < (parameters->plotArea.right - xGridSpacing * 3 / 4)) { markerPosition += xGridSpacing; // check if marker is too close to the Y Axis if (yAxisX > 0 && (abs(markerPosition - yAxisX) < min(10, xGridSpacing / 2))) continue; MoveToEx(hdc, markerPosition, xAxisY - AxisMarkerLength / 2, NULL); LineTo(hdc, markerPosition, xAxisY + AxisMarkerLength / 2); } // drawArrow arrowX = (int)round(parameters->plotArea.right + AxisArrowLength * cos(90 + AxisArrowAngle)); arrowY = (int)round(AxisArrowLength * sin(AxisArrowAngle)); MoveToEx(hdc, parameters->plotArea.right, xAxisY, NULL); LineTo(hdc, arrowX, xAxisY - arrowY); MoveToEx(hdc, parameters->plotArea.right, xAxisY, NULL); LineTo(hdc, arrowX, xAxisY + arrowY); } if (yAxisX > 0) { markerPosition = parameters->plotArea.top; while (markerPosition + yGridSpacing < (parameters->plotArea.bottom - yGridSpacing * 3 / 4)) { markerPosition += yGridSpacing; // check if marker is too close to the X Axis if (xAxisY > 0 && (abs(markerPosition - xAxisY) < (min(10, yGridSpacing / 2)))) continue; MoveToEx(hdc, yAxisX - AxisMarkerLength / 2, markerPosition, NULL); LineTo(hdc, yAxisX + AxisMarkerLength / 2, markerPosition); } // drawArrow arrowY = (int)round(parameters->plotArea.top - AxisArrowLength * cos(90 + AxisArrowAngle)); arrowX = (int)round(AxisArrowLength * sin(AxisArrowAngle)); MoveToEx(hdc, yAxisX, parameters->plotArea.top, NULL); LineTo(hdc, yAxisX - arrowX, arrowY); MoveToEx(hdc, yAxisX, parameters->plotArea.top, NULL); LineTo(hdc, yAxisX + arrowX, arrowY); } SelectObject(hdc, previousPen); DrawMarkerValues(hdc, parameters, xGridSpacing, yGridSpacing, xAxisY, yAxisX); } void DrawMarkerValues(HDC hdc, const PlotParameters* parameters, int xGridSpacing, int yGridSpacing, int xAxisY, int yAxisX) { static HFONT font; HFONT previousFont; int markerPosition; TCHAR stringValue[128]; RECT valueRect; font = CreateFont(-MulDiv(AxisMarkerFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, FW_NORMAL, //grosime FALSE, //italic FALSE, //underline FALSE, //strikeout DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FF_DONTCARE, AxisMarkerFont); previousFont = (HFONT)SelectObject(hdc, font); if (xAxisY > 0) { markerPosition = parameters->plotArea.left; while (markerPosition + xGridSpacing < (parameters->plotArea.right - xGridSpacing * 3 / 4)) { markerPosition += xGridSpacing; valueRect.left = markerPosition - xGridSpacing / 2; valueRect.right = markerPosition + xGridSpacing / 2; valueRect.top = xAxisY + AxisMarkerLength; valueRect.bottom = parameters->plotArea.bottom; double value = (double)(markerPosition - parameters->plotArea.left) / (double)(parameters->plotArea.right - parameters->plotArea.left) * (parameters->xTo - parameters->xFrom) + parameters->xFrom; if (fabs(value) < (parameters->xGridSpacing / 4.0)) { value = 0; valueRect.left -= 20; _stprintf(stringValue, _T("%d"), 0); } else { _stprintf(stringValue, parameters->xMarkerFormat, value); } DrawText(hdc, stringValue, -1, &valueRect, DT_CENTER | DT_TOP); } } if (yAxisX > 0) { markerPosition = parameters->plotArea.top; while (markerPosition + yGridSpacing < (parameters->plotArea.bottom - yGridSpacing * 3 / 4)) { markerPosition += yGridSpacing; valueRect.left = parameters->plotArea.left; valueRect.right = yAxisX - AxisMarkerLength; valueRect.top = markerPosition - yGridSpacing / 2; valueRect.bottom = markerPosition + yGridSpacing / 2; double value = parameters->yTo - (double)(markerPosition - parameters->plotArea.top) / (double)(parameters->plotArea.bottom - parameters->plotArea.top) * (parameters->yTo - parameters->yFrom); if (fabs(value) < (parameters->yGridSpacing / 10.0)) continue; _stprintf(stringValue, parameters->yMarkerFormat, value); DrawText(hdc, stringValue, -1, &valueRect, DT_RIGHT | DT_SINGLELINE | DT_VCENTER); } } SelectObject(hdc, previousFont); } Pasul 6 - DrawFunction Pentru fiecare funcție de reprezentat se apelează DrawFunction. Aceasta funcționează în felul următor:
void DrawFunction(HDC hdc, const PlotAction* action, const PlotParameters* parameters) { HPEN pen, previousPen; double x, y; int xCoord, yCoord; BOOL inArea = FALSE; pen = CreatePen(action->style, 1, action->color); previousPen = (HPEN)SelectObject(hdc, pen); for (xCoord = parameters->plotArea.left; xCoord <= parameters->plotArea.right; xCoord++) { x = parameters->xFrom + (double)(xCoord - parameters->plotArea.left) / (parameters->plotArea.right - parameters->plotArea.left) * (parameters->xTo - parameters->xFrom); y = action->function(x); if (isnan(y) || isinf(y) || y < parameters->yFrom || y > parameters->yTo) { // cannot display this point inArea = FALSE; continue; } yCoord = (int)round(parameters->plotArea.bottom - (double)(y - parameters->yFrom) / (parameters->yTo - parameters->yFrom) * (parameters->plotArea.bottom - parameters->plotArea.top)); if (inArea == FALSE) { MoveToEx(hdc, xCoord, yCoord, NULL); inArea = TRUE; } else { LineTo(hdc, xCoord, yCoord); } } SelectObject(hdc, previousPen); } Pasul 6 - DrawLegend Legenda joacă și ea un rol important. Ea este amplasată în colțul din dreapta sus și prezintă o particularitate interesantă: dimensiunea chenarului de legentă depinde de lungimea maximă a mesajului ce identifică funcțiile de reprezentat. Această lungime se poate calcula însă ușor cu ajutorul funcției GetTextExtentPoint32. void DrawLegend(HDC hdc, PlotAction actions[], const PlotParameters* parameters) { int numberOfPlots = 0, legendWidth, legendHeight, maxTextWidth = 0, maxTextHeight = 0; HPEN pen, previousPen; HBRUSH brush, previousBrush; static HFONT font; HFONT previousFont; SIZE currentTextSize; RECT textPosition, identifierPosition; int i; for (i = 0; actions[i].function != NULL; i++) { numberOfPlots++; } font = CreateFont(-MulDiv(LegendFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, FW_NORMAL, //grosime FALSE, //italic FALSE, //underline FALSE, //strikeout DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FF_DONTCARE, LegendFont); previousFont = (HFONT)SelectObject(hdc, font); for (i = 0; i < numberOfPlots; i++) { GetTextExtentPoint32(hdc, actions[i].legend, _tcslen(actions[i].legend), ¤tTextSize); if (currentTextSize.cx > maxTextWidth) { maxTextWidth = currentTextSize.cx; } if (currentTextSize.cy > maxTextHeight) { maxTextHeight = currentTextSize.cy; } } legendWidth = LegendItemWidth + 20 + maxTextWidth + 2 * LegendWidthSpacing; legendHeight = numberOfPlots * maxTextHeight + 2 * LegendHeightSpacing; pen = CreatePen(LegendStyle, 1, PlotAreaBorderColor); brush = CreateSolidBrush(LegendBackgroundColor); previousPen = (HPEN)SelectObject(hdc, pen); previousBrush = (HBRUSH)SelectObject(hdc, brush); Rectangle(hdc, parameters->plotArea.right - legendWidth, parameters->plotArea.top, parameters->plotArea.right, parameters->plotArea.top + legendHeight); pen = CreatePen(PS_SOLID, 1, LegendTextColor); previousPen = (HPEN)SelectObject(hdc, pen); SetBkMode(hdc, TRANSPARENT); for (i = 0; i < numberOfPlots; i++) { textPosition.top = parameters->plotArea.top + maxTextHeight * i + LegendHeightSpacing; textPosition.bottom = textPosition.top + maxTextHeight; textPosition.left = parameters->plotArea.right - legendWidth; textPosition.right = parameters->plotArea.right - LegendWidthSpacing; DrawText(hdc, actions[i].legend, -1, &textPosition, DT_RIGHT | DT_BOTTOM); identifierPosition.top = textPosition.top + maxTextHeight / 4; identifierPosition.bottom = textPosition.bottom - maxTextHeight / 4; identifierPosition.left = parameters->plotArea.right - legendWidth + LegendWidthSpacing; identifierPosition.right = identifierPosition.left + LegendItemWidth; brush = CreateSolidBrush(actions[i].color); FillRect(hdc, &identifierPosition, brush); } SelectObject(hdc, previousPen); SelectObject(hdc, previousBrush); SelectObject(hdc, previousFont); } Derivarea funcțiilor O aplicabilitate interesantă o reprezintă posibilitatea reprezentării unei funcții f'(x) care este derivata unei funcții f(x), fără însă a fi cunoscut/specificat codul funcției derivate. Quote Cum se poate una ca asta? Foarte simplu, pornind de la definiția unei derivate, luând un h suficient de mic pentru o prezicie decentă, putem calcula valoarea derivatei în orice punct, având la dispoziție doar funcția f(x). Astfel am creat o funcție de același tip ca precendentele (pentru a putea fi reprezentată folosind acelasi cod), ce calculează valoarea derivată în orice punct. Pentru a specifica funcția ce se dorește derivată am introdus o variabilă globală, derivativeFrom, ce este un pointer la funcția ce se dorește derivată. double derivative(double x) { double h = 1e-6; FunctionType f = derivativeFrom; return (f(x + h) - f(x - h)) / 2 / h; } Figura de mai jos arată graficul lui f(x) și f'(x) pentru o funcției mai complicată. Ce se poate observa? Peste tot unde f(x) are un maxim/minim local, f'(x) este 0. derivata.png 18.85K 213 downloads |
#2
Posted 22 February 2014 - 13:55
Mai bine folosești o librărie gen Cinder sau openFrameworks....
|
#6
Posted 03 May 2014 - 22:41
Un punct specific functiei va fi reprezentat doar de un pixel?
|
#7
Posted 03 May 2014 - 22:45
Da, dar daca ai 2-3 puncte foarte apropiate, posibil ca toate sa fie reprezentate de acelasi pixel
|
Anunturi
▶ 0 user(s) are reading this topic
0 members, 0 guests, 0 anonymous users