Zobacz temat - Pomoc w programie C++
 
Forum Klubu ROVERki.pl

Offtopic - Pomoc w programie C++

DrMambo - Pon Cze 21, 2010 13:46
Temat postu: Pomoc w programie C++
witam,

mam jako zadanie w szkole zrobić w programie Dev-C++ kolorowanie okręgu.
Nie za bardzo się na tym znam. Mam program który koloruje prostokąt. Może ktoś na podstawie tego wie jak napisać program, który narysuje okrąg i go pokoloruje.
Wydaje mi się, że dla znawcy to zapewne 5 min roboty.
Z góry dziękuję za jakiekolwiek podpowiedzi.

#include <windows.h>
void linia(const int x1, const int y1, const int x2, const int y2, HWND hwnd)

{
HDC hdcOkno = GetDC (hwnd);
int d, dx, dy, ai, bi, xi, yi;
int x=x1, y=y1;


if (x1<x2)
{
xi=1;
dx=x2-x1;
}

else {xi=-1, dx=x1-x2;}

if (y1<y2)
{
yi=1;
dy=y2-y1;
}
else {yi=-1; dy=y1-y2;}

SetPixel (hdcOkno, x, y, 0xFF0000);

if (dx>dy)
{
ai = (dy-dx)*2;
bi=dy*2;
d=bi-dx;

while (x!=x2)

{
if (d>0){x+=xi; y+=yi; d+=ai;}
else{d+=bi; x+=xi;}

SetPixel (hdcOkno, x, y, 0xFF0000);
}
}

else {

ai=(dx-dy);
bi=dx*2;
d=bi-dy;

while(y != y2)
{
if (d>0){x+=xi; y+=yi; d+=ai;}
else {d+=bi; y+=yi;}

SetPixel (hdcOkno, x, y, 0xFF0000);
}
}
}

void fill4(HDC hdc, int x, int y, int cedge, int col) {
int color = GetPixel(hdc, x, y);
if ((color != cedge) && (color != col)) {
SetPixel(hdc, x, y, col);
fill4(hdc, x,y-1,cedge,col);
fill4(hdc, x,y+1,cedge,col);
fill4(hdc, x-1,y,cedge,col);
fill4(hdc, x+1,y,cedge,col);
}
}

/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/* Make the class name into a global variable */
char szClassName[ ] = "WindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)

{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */

/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);

/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default color as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;

/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Windows App", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
600, /* The programs width */
600, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);

/* Make the window visible on the screen */
ShowWindow (hwnd, nFunsterStil);

HDC hdc = GetDC(hwnd);

int x1, y1, x2, y2 ;

linia(30, 30, 330, 30 , hwnd);
linia(330, 30, 330, 330 , hwnd);
linia(330, 330, 180, 180 , hwnd);
linia(180, 180, 30, 330 , hwnd);
linia(30, 330, 30, 30 , hwnd);


fill4(hdc, 40, 40, 0xFF0000, 0x00FF0000);

ReleaseDC(hwnd, hdc); UpdateWindow(hwnd);


/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}

/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}


/* This function is called by the Windows function DispatchMessage() */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}

return 0;

marcingv - Pon Cze 21, 2010 20:54

Przerobiłem Ci tak na szybko - rysuje niebieski obrys i koloruje go na różowo: http://pastie.org/1014125 :)
DrMambo - Wto Cze 22, 2010 07:16

Wielkie dzięki ... jestem :lol: Jakby jeszcze Ci się nudziło albo Znalazłbyś 3 min. czasu i ew jakiś opis przy tym swoim kodzie dopisał .. albo ogólnie napisał jaką metodą to się dzieje .. ten mój wielobok kolorowało funkcją fill .. a ten okrąg ?? Jeszcze raz wielkie dzięki .. jak życie pozwoli to jakoś się odwdzięczę .. bije pokłony :)
marcingv - Wto Cze 22, 2010 16:03

A więc tak, w kodzie który Ci przesłałem są dwie główne funkcje - obys() oraz wypelnij(). Obie funkcje przyjmują jako parametr promień koła które mają narysować/pokolorować. Sam okrąg jest reprezentowany jako zbiór par (x, y) rozwiązań funkcji (x-xs)^2 + (y-ys)^2 = r^2, gdzie (xs, ys) to współrzędne okręgu a 'r' to promień. Koło to zbiór par rozwiązań wzoru (x-xs)^2 + (y-ys)^2 <= r^2. Okno w którym rysowane jest koło/okrąg można traktować jako układ wspólrzędnych mający początek w lewym górnym rogu - (0,0) a jego koniec jest w prawym dolnym rogu - (szerokoscOkna, wysokoscOkna). Obie funkcje rysują koło/okrąg z uwzględnieniem lewego i górnego marginesu od krawędzi okna i wynosi on 50px. Wiemy, że okrąg to zbiór par (x, y), oraz znając promień koła - wiemy ze te pary mają postać ({1, 2, 3 ... 2*promien}, y), po uwzględnieniu marginesów pary mają postać ({1, 2, 3 ... 2*promien}+50, y). Współrzędne środka takiego okręgu mają postać (promien+50, promien+50). W tym momencie mamy wszystkie dane potrzebne do wzoru, tzn wiemy w jakim zakresie zmieniaja się 'iksy' (50 do 2*promien+50), znamy współrzędne środka, pozostaje przekształcić wzór tak aby móc dla każdego 'iksa' wyliczyć 'igreka'. Dla każdego 'iksa' funkcja daje nam dwa rozwiązania które trzeba uwzględnić. Każdą wyliczoną parę (x,y) rysuję w oknie za pomocą metody SetPixel(). W funkcji obrys() wyliczam i rysuję osobno dwa zbiory rozwiązań ({1, 2, 3 ... 2*promien}+50, y) oraz (x, {1, 2, 3 ... 2*promien}+50) ze względu na to, żeby krawędzie koła nie miały w niektórych miejscach przerw związanych z rozdzielczością ekranu. Kolorowanie koła sprowadza się do znalezienia takich par (x, y) które spełniają równanie (x-xs)^2 + (y-ys)^2 <= r^2.

Wywołanie funkcji następuje tak:
Kod:

//Rysowanie obrysu kola i wypelnianie go kolorem
int promien = 200; //zmienna przechowujaca promien kola
obrys(promien, hwnd); //rysuje okrąg o podanym promieniu
wypelnij(promien, hwnd); //koloruję koło o podanym promieniu


Nie wiem czy napisałem to w miarę zrozumiale, trudno jest mi to wytłumaczyć tak dokładnie w formie tekstowej :P Tutaj znajduje się kod źródłowy do którego dodałem jeszcze parę komentarzy: http://www.wklejto.pl/70718 :)

DrMambo - Sro Cze 23, 2010 06:46

A Dałbyś radę np zmienić to tak żeby okrąg wypełniało liniami .. np przez powielenie promienia od godziny 12 lub jakiejś innej .. bo chyba mam zadanie wypełnić okrąg liniami :/ wiem pałka jestem, że od razu nie spojrzałem .. :roll:

[ Dodano: Sro Cze 23, 2010 07:45 ]
Chyba, że także w Twoim kodzie można powiedzieć, że okrąg jest wypełniany tak samo jak w tym pseudo moim ..

[ Dodano: Sro Cze 23, 2010 07:46 ]
to może przejdzie ..

marcingv - Sro Cze 23, 2010 10:43

Tak czy siak narysowanie linii wiąże się z narysowaniem każdego punktu z osobna który do niej należy. Ale można powiedzieć, że moja funkcja właśnie wypełnia okrąg liniami, zaczynając od lewej strony okręgu. Poruszam się po poziomej średnicy kola, czyli tak jakbym poruszał się po osi oX w układzie, i dla każdego 'iksa' obliczam dwa rozwiązania, dwa punkty y1 i y2 które należą do obwodu koła, następnie między tymi punktami rysuję linię. Na obrazku poniżej widać jak to działa. Każdy krok pętli for() w funkcji wypelnij() narysowałem osobnym kolorem:



Czyli jak widać w mojej funkcji koło jest kolorowane 'pionowymi liniami' zaczynając od lewej strony okręgu.

Ale nic nie stoi na przeszkodzie aby użyć dokładnie tej samej funkcji która jest w Twoim przykładzie. Wystarczy do kodu źródłowego wkleić kod funkcji fill4() i w ten sposób wywołać funkcje w kodzie:

Kod:

//Rysowanie obrysu kola i wypelnianie go kolorem
int promien = 200; //zmienna przechowujaca promien kola
obrys(promien, hwnd); //rysuje okrąg o podanym promieniu
fill4(hdc, promien+50, promien+50, 0xFF0000, 0x00FF0000);


W ten sposób okrąg zostanie pokolorowany w identyczny sposób jak figura z Twojego przykładu. Kod źródłowy z wersją która koloruje okrąg funkcją fill4() masz tutaj: http://pastie.org/1015623

Jeżeli chodzi o wady i zalety obu z tych funkcji do kolorowania, to zdecydowanie lepszą do pokolorowania koła jest moja funkcja. Dlaczego? Ponieważ złożoność obliczeniowa i pamięciowa mojej funkcji jest zdecydowanie mniejsza. Przykładowo, aby pokolorować okrąg o promieniu 200px za pomocą mojej funkcji, potrzebnych jest 125600 kroków, zaś za pomocą funkcji fill4() aby pokolorować okrąg potrzebnych jest 125600 * 125600 * 125600 * 125600 kroków :P Zresztą różnice w szybkości kolorowania wyraźnie widać jak się uruchomi program używając raz jednej a raz drugiej funkcji. Wadą mojej funkcji jest to, że można nią pokolorować tylko okrąg, a funkcją fill4() można pokolorować każdą figurę. Także są plusy i minusy obu rozwiązań. W razie czego możesz kolorowanie zrobić tak jak było to w przykładzie, tą funkcją fill4() :)

DrMambo - Sro Cze 23, 2010 22:54

Jakbym mógł to bym zaznaczył milion razy pomógł .. jesteś wielki ... wieeeeeeeelkie dzięki .. przy okazji stawiam sześciopak .. może kiedyś się stykniemy na jakimś zlocie itp .. dziękuję jeszcze raz i pozdrawiam

[ Dodano: Sro Cze 23, 2010 23:54 ]
dopisze jeszcze bo mam 2 znajomego co mi przesłał sama funkcje rysowania okręgu ..

http://pastie.org/1016284

void kolko(const HWND hwnd, const int x, const int y, const int radius, COLORREF color) {
MSG messages;
HDC hDC = GetDC(hwnd);

// punkt srodkowy (niebieski)
SetPixel(hDC, x, y, RGB(0, 0, 255));

//for (float r = 0.0; r <= 360.0; r += 0.25) { // rysowanie zgodnie z ruchem wskazowek zegara
//for (float r = 360.0; r >= 0.0; r -= 0.25) { // rysowanie odwrotnie do ruchu wskazowek zegara
for (float r = 0.0; r <= 360.0; r += 0.25) {
Sleep(1); //zeby bylo widac rysowanie

SetPixel(
hDC,
x + sin((r * M_PI) / 180.0) * radius,
y - cos((r * M_PI) / 180.0) * radius,
RGB(0, 0, 255)
);

}
}

czy tak nie było by prościej ??

marcingv - Sro Cze 23, 2010 23:22

Ten sposób rysowania okręgu też jest dobry :) Ogólnie można to zrobić właśnie na dwa sposoby, pierwszy poprzez obliczenie punktów należących do obwodu koła korzystając z równania okręgu czyli tak jak ja to zrobiłem, lub tak jak zrobił to Twój znajomy - poprzez obrót punktu o 360stopni względem środka okręgu. Oba sposoby są dobre a ich złożoności obliczeniowe są bardzo do siebie zbliżone.

Trudno powiedzieć które rozwiązanie jest prostsze, zależy kto którą metodę lepiej rozumie :) Ja osobiście nie lubię trygonometrii i wolę metodę z równaniem okręgu :P Ale chyba łatwiejszy w zrozumieniu jest sposób z obrotem punktu wokół środka okręgu :)

DrMambo - Czw Cze 24, 2010 07:43

to gdyby skleić to rysowanie koła i kolorowanie tą funkcją fill to jak powinien wyglądać cały kod ?? :) bo coś mnie nie idzie to sklejenie 2 części :/
marcingv - Czw Cze 24, 2010 09:16

Proszę tutaj już sklejone: http://pastie.org/1016898

Dodałem w funkcji Twojego znajomego rzutowanie parametrów funkcji SetPixel() do int, bo inaczej by się nie kompilowało :)

DrMambo - Sro Cze 30, 2010 08:51

Napiszę jeszcze jedno pytanko .. dotyczy 1 kodu, który Napisałeś. "Poruszam się po poziomej średnicy kola, czyli tak jakbym poruszał się po osi oX w układzie, i dla każdego 'iksa' obliczam dwa rozwiązania, dwa punkty y1 i y2 które należą do obwodu koła, następnie między tymi punktami rysuję linię" czy ta metoda ma jakąś nazwę ? czy to po prostu zastosowanie tej pętli for ?

[ Dodano: Sro Cze 30, 2010 09:51 ]
SetPixel()??

marcingv - Czw Lip 01, 2010 09:43

Ta metoda nazywa się 'wypelnij()'. Wewnątrz tej funkcji jest pętla for() która oblicza wszystkie pary rozwiązań równania okręgu i między tymi parami punktów rysuje linie.

Kod:

for(int i=50; i<srednica+50; i++) {
         xT = i;
         yT = (int)sqrt(promien * promien - (xT-Sx)*(xT-Sx) ) + Sy;

         for(int j=0; j<=yT-promien-50; j++) {
             SetPixel (hdcOkno, xT, yT-j, 0xFF00FF);
         }

         int tmp = abs(yT-2*promien - 50)+50;
         for(int j=0; j<yT-promien-50; j++) {
             SetPixel (hdcOkno, xT, tmp+j, 0xFF00FF);
         }

     }


Realizuje to powyższy kod (częśc funkcji 'wypelnij'). Najbardziej zewnętrzny for odpowiada za poruszanie się po osi oX. Dla kazdego xT obliczam dwie pary rozwiazan: yT oraz tmp. Te dwie wewnętrzne pętle for() odpowiadają za narysowanie linii pomiędzy punktami yT oraz tmp. Rysowanie odbywa się tak: w pierwszym for() rysowane są piksele zaczynając od punktu należącego do górnej krawędzi koła (yT) i schodząc w dół aż dojdę do poziomej średnicy koła; drugi for() rysuje piksele od poziomej średnicy w dół aż do punktu należącego do dolnej krawędzi koła (tmp). W efekcie koncowym rysowana jest linia pomiędzy punktami yT a tmp.

Funkcja SetPixel() jest to gotowa funkcja z bibliotek systemowych która wstawia pixel o zadanym kolorze w punkcie (x,y)

DrMambo - Sob Lip 03, 2010 11:55

:lol: zaliczone :lol: