در درس قبل با initwindow, putpixel, line, circle, rectangle و setcolor آشنا شدیم. در این درس از چند تابع جدید نیز استفاده میکنیم:
bar(x1, y1, x2, y2) — مستطیل توپر با رنگ فعلی.
fillellipse(x, y, rx, ry) — بیضی توپر با شعاعهای افقی rx و عمودی ry.
setfillstyle(pattern, color) — تنظیم الگو و رنگ توپر. الگوی SOLID_FILL (مقدار ۱) سادهترین حالت است.
delay(ms) — توقف بهمدت ms میلیثانیه (برای انیمیشن).
kbhit() — بررسی اینکه آیا کلیدی فشرده شده (مقدار غیرصفر) یا خیر. در حلقه while(!kbhit()) برنامه تا فشردن کلید منتظر میماند.
دایره محاطی در مربع و مستطیل
اگر یک مربع با مرکز (x, y) و نیمضلع r داشته باشیم، دایره محاطی در آن، دایرهای است که به تمام چهار ضلع مربع مماس باشد. شعاع این دایره برابر r و مرکز آن (x, y) است.
main.cppcpp
برای مستطیل محاطی (با ابعاد متفاوت)، باید از شعاع کوچکتر استفاده کنیم. اگر ضلعهای مستطیل 2a و 2b باشند، شعاع دایره محاطی برابر است با min(a, b).
دایره محاطی در مربع
رسم مربع و دایره محاطی در آنcpp
اصلاح سینتکس rectangle در جزوه
در کد اصلی جزوه برای رسم مستطیل با ضلع کوچکتر (r/d که در آن d = √2)، دستوری بهصورت rectangle(x-r/d, y-r/d, x+r(d), y+r(d)) نوشته شده بود که دارای دو خطای نگارشی است: نبود عملگر * بین r و (d)، و پرانتزگذاری اشتباه.
نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاحشده و استاندارد
کد صحیحcpp
تقسیم مثلث به n قسمت
فرض کنید مثلثی با دو رأس پایین (x1, y) و (x2, y) و یک رأس بالا (x, Y) داریم. میخواهیم با خطوطی که از رأس بالا به نقاط روی ضلع پایین کشیده میشوند، مثلث را به n قسمت تقسیم کنیم.
روش:
طول ضلع پایین: L = x2 - x1
نقطه میانی پایین: x = (x1 + x2) / 2
در حلقه از i = 1 تا n، نقاط تقسیم روی ضلع پایین را محاسبه میکنیم و خطوطی از رأس بالا به آنها میکشیم.
در نسخه جزوه از فرمول Y = y - i * L / 2 برای محاسبه ارتفاع رأس بالا استفاده شده بود که در این پیادهسازی آن را به شکل استانداردتر بازنویسی میکنیم.
تقسیم مثلث به n قسمت
رسم مثلث و تقسیم آن با خطوط از رأس بالاcpp
فلوچارت تقسیم مثلث
flow.svg
تولید اعداد تصادفی: rand و srand
برای تولید اعداد تصادفی در C++ از تابع rand() در هدر <cstdlib> استفاده میشود. این تابع عدد صحیحی بین 0 تا RAND_MAX (معمولاً ۳۲۷۶۷) برمیگرداند.
محدود کردن بازه: برای تولید عدد در بازه [a, b] از فرمول زیر استفاده میکنیم:
main.cppcpp
مثال: عدد بین ۱۰ تا ۱۵:
main.cppcpp
مشکل تکرار: بدون srand، در هر بار اجرا دنباله یکسانی تولید میشود. برای تولید واقعاً تصادفی در هر اجرا، باید با زمان سیستم seed کنیم:
main.cppcpp
تابع time(NULL) تعداد ثانیههای گذشته از سال ۱۹۷۰ را برمیگرداند و در هر اجرا متفاوت است.
رسم دایرههای تصادفی رنگی
رسم دایرههای با موقعیت و رنگ تصادفیcpp
چندضلعی منتظم (n-ضلعی)
برای رسم چندضلعی، کافی است n رأس را به ترتیب با خطوط بههم وصل کنیم و در پایان رأس آخر را به رأس اول وصل کنیم تا شکل بسته شود.
اگر مختصات رئوس از قبل داده شده باشد، الگوریتم ساده است:
آرایههای x[] و y[] از ورودی بگیر.
برای i از ۰ تا n-2: line(x[i], y[i], x[i+1], y[i+1]).
در پایان: line(x[n-1], y[n-1], x[0], y[0]).
نکته آرایهها: در این مثال از آرایههای پویا استفاده میکنیم. در C++ مدرن از std::vector استفاده میشود که در تمرینها نشان داده شده است.
رسم چندضلعی از روی نقاط
رسم چندضلعی n-ضلعی با مختصات ورودیcpp
دایرههای هممرکز
برای رسم چند دایره هممرکز، در یک حلقه شعاع را افزایش میدهیم و circle را با همان مرکز اما شعاع متفاوت فراخوانی میکنیم.
برای رسم یک شبکه از دستههای دایره هممرکز (مثلاً m ردیف و k ستون، هر دسته شامل n دایره)، از سه حلقه تودرتو استفاده میکنیم:
حلقه بیرونی: ردیفها (m)
حلقه میانی: ستونها (k)
حلقه درونی: دایرههای هر دسته (n)
بعد از رسم هر دسته، مرکز را به اندازه قطر کل دسته بهعلاوه یک فاصله به سمت راست جابجا میکنیم. در پایان هر ردیف، مرکز به نقطه شروع برمیگردد و به سمت پایین جابجا میشود.
شبکه دایرههای هممرکز
رسم m×k دسته از n دایره هممرکزcpp
اصلاح کد شبکه دایرهها در جزوه
کد اصلی جزوه دارای چند اشکال بود: استفاده از حرف بزرگ I بهعنوان شمارنده (که با عدد ۱ اشتباه گرفته میشود)، جابجایی نادرست x و نبود مدیریت ردیفها. در نسخه اصلاحشده از نامهای معنادارتر row, col, i استفاده شده و مدیریت افقی و عمودی بهدرستی تفکیک شده است.
نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاحشده و استاندارد
کد صحیحcpp
استفاده از kbhit برای انیمیشن
تابع kbhit() (از <conio.h>) بررسی میکند آیا کلیدی فشرده شده است. این تابع در ساخت انیمیشنهای گرافیکی مفید است:
main.cppcpp
به این ترتیب تا فشردن کلید، انیمیشن اجرا میشود.
یادآوری: graphics.h قدیمی است
تمام کدهای این درس به کتابخانه graphics.h وابستهاند که روی کامپایلرهای استاندارد مدرن موجود نیست. اگر کد بالا را اجرا نمیکند، دلایل احتمالی:
کامپایلر شما graphics.h را ندارد → باید WinBGIm را نصب کنید یا از Dev-C++ قدیمی استفاده کنید.
روی لینوکس/مک → عملاً اجرا نمیشود مگر با Wine یا کتابخانههای شبیهساز.
در کدبلاکها (Code::Blocks) → باید مسیر کتابخانه را بهصورت دستی اضافه کنید.
پیشنهاد: برای یادگیری واقعی گرافیک، همان کدها را با raylib یا SFML بازنویسی کنید (درس قبلی بهعنوان جایگزین معرفی شدند). منطق الگوریتمها یکسان است، فقط API فرق میکند.
برنامهای بنویسید که یک مربع با ضلع `2r` حول مرکز `(x, y)` و دایره محاطی در آن رسم کند. سپس مربعی کوچکتر با ضلع `2r/√2` (دایره محاطی کوچکتر) هم رسم کند.
نمایش راهنما
برای √2 از `sqrt(2.0)` استفاده کنید و شعاع کوچکتر `r/sqrt(2)` را برای دایره داخلی بهکار ببرید.
02
تمرین•حل کامل دارد
برنامهای بنویسید که n دایره تصادفی با موقعیت، شعاع و رنگ تصادفی رسم کند. شعاع بین ۱۰ تا ۵۰ و رنگ بین ۱ تا ۱۵ باشد. بین هر دایره ۲۰۰ میلیثانیه مکث کنید.
نمایش راهنما
از `srand(time(NULL))`، `rand() % range + min` و `delay(200)` استفاده کنید.
03
تمرین•حل کامل دارد
برنامهای بنویسید که یک چندضلعی منتظم n-ضلعی را با محاسبه مختصات رئوس روی یک دایره رسم کند. مرکز (۴۰۰، ۳۰۰) و شعاع ۲۰۰ باشد.
نمایش راهنما
زاویه هر رأس: `theta = 2 * M_PI * i / n`. مختصات: `x = cx + r * cos(theta)`، `y = cy + r * sin(theta)`. از `<cmath>` برای sin و cos استفاده کنید.
04
تمرین•حل کامل دارد
برنامهای بنویسید که مثلثی متساویالاضلاع رسم کند و سپس آن را با سه میانه به ۶ مثلث هممساحت تقسیم کند. (نکته: میانه، خطی از هر رأس تا نقطهی میانی ضلع مقابل است؛ سه میانه همدیگر را در مرکز ثقل — میانگین مختصات سه رأس — قطع میکنند.)
نمایش راهنما
برای مثلث متساویالاضلاع با مرکز (cx, cy) و شعاع محیطی r، سه رأس در زاویههای ۰، ۱۲۰ و ۲۴۰ درجه قرار دارند. نقطهی میانی هر ضلع را با میانگین مختصات دو سرش حساب کنید و هر رأس را به نقطهی میانی ضلع مقابل وصل کنید؛ سه میانهی کامل مثلث را دقیقاً به ۶ ناحیه تقسیم میکنند.
// نکات-کلیدی۶
01برای رسم دایره محاطی در مربع، شعاع دایره برابر نیمضلع مربع و مرکز آن با مرکز مربع منطبق است.
02برای تقسیم مثلث به n قسمت با خطوط از یک رأس، نقاط تقسیم روی ضلع مقابل را با فرمول `px = x1 + (x2-x1)*i/n` محاسبه میکنیم.
03برای تولید عدد تصادفی در بازه `[a, b]` از فرمول `a + rand() % (b - a + 1)` و برای عدم تکرار از `srand(time(NULL))` استفاده کنید.
04چندضلعی منتظم n-ضلعی را میتوان با محاسبه رئوس روی دایره (`cos`, `sin` با زاویه `2π*i/n`) رسم کرد.
05برای شبکهای از دستههای دایره هممرکز از سه حلقه تودرتو استفاده میشود: ردیف، ستون، و دایرههای هر دسته.
06کتابخانه `graphics.h` قدیمی است؛ برای پروژههای واقعی از raylib یا SFML استفاده کنید و منطق الگوریتمها را به آنها منتقل کنید.
گام بعدی پیشنهادی
ماتریسها و عملیات ماتریسی
پیمایش، ضرب، یافتن ماکزیمم سطر، ماتریس مثلثی و تولید تصادفی
نام درس، موضوع یا کلمه کلیدی را بنویسید و با Enter به آن بروید
rectangle(x - r, y - r, x + r, y + r);// مربعcircle(x, y, r);// دایره محاطی
1#include<iostream>2#include<graphics.h>3#include<conio.h>4usingnamespace std;56intmain(){7int x, y, r;8 cout <<"x y r: ";9 cin >> x >> y >> r;1011initwindow(800,600);1213setcolor(WHITE);14rectangle(x - r, y - r, x + r, y + r);// مربع1516setcolor(YELLOW);17circle(x, y, r);// دایره محاطی1819getch();20closegraph();21return0;22}23
1// دارای خطای سینتکس در جزوه2double d =sqrt(2);3rectangle(x - r/d, y - r/d, x +r(d), y +r(d));// خطا4// مشکل ۱: r(d) به جای r*d5// مشکل ۲: این فقط یک مستطیل کوچکتر رسم میکند، نه یک دایره
1// نسخه اصلاحشده2#include<cmath>3double d =sqrt(2.0);4// مربع اصلی با ضلع 2r5rectangle(x - r, y - r, x + r, y + r);6// مستطیل (در واقع مربع) کوچکتر با ضلع 2r/d7rectangle(x -(int)(r/d), y -(int)(r/d),8 x +(int)(r/d), y +(int)(r/d));9// دایره محاطی در مستطیل بزرگ10circle(x, y,(int)(r/d));11
1#include<iostream>2#include<graphics.h>3#include<conio.h>4usingnamespace std;56intmain(){7double x1, y, x2, Ytop;8int n;9 cout <<"x1 y x2 Ytop: ";10 cin >> x1 >> y >> x2 >> Ytop;11 cout <<"n: ";12 cin >> n;1314initwindow(800,600);1516// رسم ضلع پایین مثلث17line((int)x1,(int)y,(int)x2,(int)y);1819// رسم دو ضلع جانبی20line((int)x1,(int)y,(int)((x1 + x2)/2),(int)Ytop);21line((int)x2,(int)y,(int)((x1 + x2)/2),(int)Ytop);2223// تقسیم ضلع پایین به n+1 قسمت مساوی و رسم خطوط به رأس بالا24setcolor(LIGHTGREEN);25for(int i =1; i <= n -1; i++){26double px = x1 +(x2 - x1)* i / n;// نقطه تقسیم روی ضلع پایین27line((int)px,(int)y,(int)((x1 + x2)/2),(int)Ytop);28}2930getch();31closegraph();32return0;33}34
flow.svg100%
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
شروع/پایانپردازشتصمیمورودی/خروجی
x = a +rand()%(b - a +1);
int x =10+rand()%6;// 6 = 15 - 10 + 1
#include<cstdlib>#include<ctime>srand(time(NULL));// در ابتدای main
1#include<iostream>2#include<graphics.h>3#include<conio.h>4#include<cstdlib>5#include<ctime>6usingnamespace std;78intmain(){9srand((unsigned)time(NULL));10initwindow(800,600);1112int n;13 cout <<"تعداد دایرهها: ";14 cin >> n;1516for(int i =0; i < n; i++){17int x =rand()%800;// 0 تا 79918int y =rand()%600;// 0 تا 59919int c =rand()%15+1;// 1 تا 1520setcolor(c);21circle(x, y,10);22delay(100);// مکث 100ms23}2425getch();26closegraph();27return0;28}29
1#include<iostream>2#include<vector>3#include<graphics.h>4#include<conio.h>5usingnamespace std;67intmain(){8int n;9 cout <<"تعداد رئوس: ";10 cin >> n;1112 vector<int>x(n),y(n);13for(int i =0; i < n; i++){14 cout <<"نقطه "<< i +1<<": ";15 cin >> x[i]>> y[i];16}1718initwindow(800,600);19setcolor(CYAN);2021// وصل نقاط به ترتیب22for(int i =0; i < n -1; i++)23line(x[i], y[i], x[i +1], y[i +1]);2425// بستن شکل: آخرین به اولین26line(x[n -1], y[n -1], x[0], y[0]);2728getch();29closegraph();30return0;31}32
1#include<iostream>2#include<graphics.h>3#include<conio.h>4usingnamespace std;56intmain(){7int startX, startY, n, m, k, r;8 cout <<"x y n m k r: ";9 cin >> startX >> startY >> n >> m >> k >> r;1011initwindow(800,600);1213for(int row =0; row < m; row++){14int x = startX;15int y = startY + row *(2* n * r + r);16for(int col =0; col < k; col++){17// رسم n دایره هممرکز در (x, y)18for(int i =1; i <= n; i++)19circle(x, y, i * r);20// جابجایی افقی به ستون بعدی21 x +=2* n * r + r;22}23}2425getch();26closegraph();27return0;28}29
1// نسخه دارای اشکال در جزوه2int x, y, n, m, k, r, x1;3cin >> x >> y >> n >> m >> k >> r;4initwindow(800,600);5x1 = x;6for(int i =1; i <= m; i++){7for(int j =1; j <= k; j++){8for(int I =1; I <= n; I++)// حرف بزرگ I9circle(x, y, I * r);10 x +=2* n * r + r;11}12 x = x1;// فقط x بازنشانی شد، y تغییر نکرد!13}
1// نسخه اصلاحشده2int startX, startY;3cin >> startX >> startY >> n >> m >> k >> r;4initwindow(800,600);5for(int row =0; row < m; row++){6int x = startX;7int y = startY + row *(2* n * r + r);// هر ردیف پایینتر8for(int col =0; col < k; col++){9for(int i =1; i <= n; i++)10circle(x, y, i * r);11 x +=2* n * r + r;// ستون بعدی12}13// x بهصورت محلی در ابتدای هر ردیف بازنشانی میشود14}
while(!kbhit()){// رسم یک فریمdelay(50);// 50 میلیثانیه صبرcleardevice();// پاک کردن صفحه}