برنامج باقة الزهور في ++C
يحتوي هذا المقال علي ثلاثة برامج , تجمع بين البهجة وبين المستوي المتقدم الذي يلقي مزيدا من الضوء علي ما للغة السي ++ والبرمجة الكائنية من إمكانيات . البرنامج الأول يستخدم فكرة "الفراكتال fractals " لإنتاج زهرة تتفتح بألوان زاهية , والثاني برنامج باسم "لعبة الحياة" يمثل نموذجا لكائن مجهري يتكاثر , أما البرنامج الثالث فهي اللعبة الشهيرة باسم ''Tic-Tac-Toc'' والتي نعرفها باسم "إكس-أو" . يستخدم البرنامج الأول إمكانيات الدوال الرسومية للغة السي++ , أما البرنامجان الأخران فيستخدمان المحارف الرسومية لجدول الأسكي .
برنامج باقة الزهور
فكرة "الفراكتال"
تتلخص فكرة "الفراكتال" في إنتاج تشكيلات ذاتية الاستنساخ من شكل أولي , أي تستطيع أن تكرر نفسها مرات ومرات بناء علي صيغة رياضية معينة .
وأفضل طريقة لفهم هذا الأسلوب من إخراج الرسومات الحاسوبية هو أعطاء مثال لها , والمثال الذي نقدمه يخرج باقة تتفتح بزهور حمراء اللون وفي كل مرة تشغيل فيها البرنامج , تحصل علي باقة جديدة
شرح البرنامج
النمط الأولي في برنامجنا غاية في البساطة , مجرد خط مستقيم , يتصل عدد من قطع منه بزاوية معينة , وبلون معين , لتكوين فروع الباقة أو أوراق الزهرة
ويتمثل تطبيق أسلوب الاستنساخ الذاتي في أن كل فرع في الباقة يتفرع عنه عدة أفرع أصغر في الطول , و يلون مختلف
والزهور حمراء أما الأفرع فهي متدرجة الألوان , تبدأ بفروع من اللون الأصفر يتفرع عنها أفرع باللون الأخضر الفاتح أصغر من الصفراء طولا , تتفرع عن أفرع أصغر باللون الأخضر , هي التي تحمل الزهور .
وإليك صياغة البرنامج , ثم نتولي شرحه بعد ذلك
Tendril.cpp
// tendrils.cpp
// draws "biological" forms
// UCS Laboratories
#include <graphics.h> // for graphics functions
#include <stdlib.h> // for randomize(), rand()
#include <time.h> // for randomize()
#include <math.h> // for sin(), cos()
#include <conio.h> // for getch()
const int X0=320; // center of screen
const int Y0=240;
const float PI=3.14159; // pi
const int NUME=30; // numerator of probability
const int DENOM=100; // denomenator of probability
const int NUMBER = 7; // number of tendrils per cluster
const float RAD = 3.0; // length of straight line segments
const float DELTHETA = 0.1; // change in theta (radians)
const int SEGS = 60; // max line segments per tendril
const int REDUX = 3; // how much to divide # of segments
const int MIN = 1; // minimum number of line segments
class cluster
{
public:
void display(int size, int x, int y);
};
class tendril
{
public:
void display(int size, float theta, int x, int y);
};
void cluster::display(int size, int x, int y)
{
if( kbhit() )
exit(0);
for(int i=0; i<NUMBER; i++) // for each tendril
{
float theta = i * 2*PI/NUMBER;
moveto(x, y);
tendril t; // make a tendril
t.display(size, theta, x, y); // display it
}
}
void tendril::display(int size, float theta, int x, int y)
{
for(int j=0; j<size; j++)
{ // left or right
int chng = ( random(DENOM)<NUME ) ? -1 : 1;
theta = theta + chng*DELTHETA; // new angle
x = x + RAD*sin(theta); // x and y of
y = y + RAD*cos(theta); // next point
if(size < 4) setcolor(RED);
else if(size < 13) setcolor(GREEN);
else if(size < 40) setcolor(LIGHTGREEN);
else setcolor(YELLOW);
lineto(x, y); // draw line
}
if( size > MIN )
{ // if tendril long enough
cluster c; // make a new cluster
int newsize = size / REDUX; // but smaller than before
c.display(newsize, x, y); // display it
}
}
void main()
{
int driver, mode;
driver = VGA; // set graphics driver
mode = VGAHI; // and graphics mode
initgraph(&driver, &mode, "\\bc45\\bgi");
randomize(); // seed random number generator
int x=X0, y=Y0; // set origin of cluster
int size = SEGS;
cluster c; // define a cluster
c.display(size, x, y); // display the cluster
getch(); // hold image until keypress
closegraph(); // reset graphics system
}
شرح خرج البرنامج :
تتكون الزهرة من سبعة أفرع صفراء , يتفرع عن كل فرع سبعة أفرع أصغر في الطول , باللون الأخضر الفاتح , يتفرع عن كب فرع سبعة أفرع أصغر باللون الأخضر , وتتفرع بدورها إلي سبعة أفرع باللون الأحمر هي أصغر الفروع , وتمثل الزهور في الشكل . (كل فرع يبلغ ثلث الفرع السابق عليه تقريبا) .
يتكون كل فرع من عدد من القطع المستقيمة يحددها المتغير size . ( وهو متغير يتناقص في قيمه بمقدار الثلث عند الانتقال من لون للأخر) , وهو المتغير الذي يحدد لون الفرع أيضا . وتتصل القطع ببعضها البعض بزاوية معينة theta , وهي تعطي الفرع شكله المتقوس .
وحتي تعطي أشكال الفراكتال إيحاءات بتمثيل الطبيعة , يجب أن تعطي لمسة من عشوائية , وفي برنامجنا هذا تكون العشوائية عن طريق جعل الزاوية theta تتأرجح لليمين تارة ولليسار تارة أخري , فتتقوس الأفرع تبعا لذلك , وقد صمم البرنامج بحيث يكون احتمال التقوس لليسار أكبر , وهو ما يفسر انحناء الشكل ككل للناحية اليسري من الشاشة .
شرح البرنامج :
يتكون البرنامج من فئتين tendril : وهي التي تتولي كائناتها رسم الأفرع و cluster وتحدد كائناتها عدد الأفرع من كل لون (العدد7) , وتتحكم في عمل كائنات الفئة الأولي .
كائنات الفئة cluater :
تحتوي علي دوارة for تنفذ 7 دورات (العدد محدد بالثابت NUMBER) كل دورة لرسم من الفروع وتقوم بالأتي :
- تحديد الزاوية بالقيمة : i*2*PI/NUMBER حيث يأخذ المتغير I يأخذ المتغير I القيم من 0 إلي 6 .
- تحريك المشيرة لنقطة بدء رسم كل فرع بالأمر :
Moveto(x,y);
- تكوين كائن t من الفئة tendril لرسم الفرع .
كما تحتوي كائنات الفئة علي تشغيل البرنامج , وهو عدم الضرب علي أي مفتاح , بالشرط if kbhit
كائنات الفئة tendril :
يتلقي كائن هذه الفئة المعلومات التالية من كائن الفئة cluster الذي أنشأه : قيمة المتغير size قيمة الزاوية theta , الإحداثيين x, y .
ويحتوي الكائن علي دوارة تقوم بعمل عدد من الدورات محدد بالقيمة size تحتوي كل دورة علي الأوامر التالية :
- تحديد شرط العشوائية بالأمر :
Int chng = (random(DENOM)<NUM) ? 1-:1;
معني لك أن المتغير chng (اختصار change) سوف يأخذ قيمة بين 1 و -1 .
- التحكم في قيمة الزاوية theta بأن يضاف إليها , أو يطرح منها قيمة صغيره هي 0.1 (محددة بالثابت (deltatheta ,والجمع أو الطرح بناء علي قيمة المتغير chng , وهي الحالة العشوائية في البرنامج .
- تحديد نقطة نهاية القطعة المستقيمة (سيأتي شرح ذلك عما قليل) .
- تحديد لون الفرع المرسوم طبقا لقيمة المتغير size وبالشروط : أكبر من 40 : أصغر , بين 40 و 13 أخضر فاتح , بين 13 و 7 أخضر , أقل من 4 أحمر .
- رسم القطعة المستقيمة من الفرع بدءا من نقطة البدء (المررة من الفئة cluster) إلي نقطة النهاية بالأمر line(x, y .
- تكرار ذلك أن ينتهي رسم الفرع بأكمله .
إذا كانت قيمة المتغير size لا تزال أكبر من 1 (محدد بالثابت MIN) تقوم الفئة بالتالي :
- إنشاء كائن من الفئة cluster حتي يبدأ رسم الفرع التالي .
- تغيير قيمة المتغير size بقسمته علي 3 (محدد بالثابت REDUX) , وإحالة هذه القيمة , وقيمة أخر نقطة في الفرع المرسوم , لكائن الفئة cluster الجديد .
تسلسل الخطوات :
- تنشئ الدالة الأصلية main كائنا c بالمعلومات التالية : عدد الدورات 7 , نقطة البدء 320 , 240 , المتغير size بالقيمة 60 .
- عند الخطوة الأولي (i=0) من الكائن c يقوم بتحديد قيمة الزاوية , وينشئ كائنا t محيلا إليه هذه القيمة , بالإضافة لقيمة نقطة البدء والمتغير size .
- يقوم الكائن t بالأتي :
· رسم الفرع بعدد 60 قطعة , وباللون الأصفر (حيث إن size أكبر من 40 )
· بعد إنتهاء الدوارة (رسم الفرع) , ولأن الشرط size>MIN متحقق , فإن الكائن يقوم بإنشاء كائن c جديد وبقسمة size علي 3 ليكون الناتج 20 ويحيل هذه القيمة إلي الكائن c المنشأ .
- يتولي الكائن الجديد c بتحديد زاوية التقوس , ثم إنشاء كائن جديد t لرسم الفرع التالي , ويكون باللون الأخضر الفاتح هذه المرة , وبعدد 20 قطعة (القيمة الجديدة للمتغير size) .
- بنفس الطريقة يرسم الفرع الأخضر (قيمة size 20/3 أي 7 مع حذف الباقي لأن size من نوع العدد الصحيح ) . ثم الفرع الأحمر (قيمة size7/3 أي 2 ) .
- لا ترسم ألوان أخري بعد اللون الأحمر , حيث تكون القيمة التالية للمتغير size هي الصفر (3/2) وهي أقل من 1 . وعلي ذلك يظل التحكم في البرنامج تحت سيطرة الكائن c الخاص برسم الأفرع الحمراء إلي أن ينتهي رسمها جميعا , فتنتهي دوارة هذا الكائن , وينتقل التحكم للكائن c الخاص باللون الأخضر .
- يقوم الكائن الأخير بإنشاء كائن t يرسم فرعا أخضر جديدا ويخلق كائنا c لرسم الأفرع الحمراء له .
- بعد انتهاء رسم كافة الأفرع الخضراء يعود التحكم للكائن الذي يرسم الأفرع الخضراء الفاتحة , لرسم فرع أخضر فاتح جديد , تتفرع عنه الفروع الباقية له .
- وهكذا إلي أن يعود التحكم للكائن c الذي أنشأته الدالة الأصلية , وهو المسئول عن رسم الأفرع الصفراء , ليبدأ رسم الفرع الأصفر الثاني (i=1) وهكذا .
المعاودة recursion
نري في البرنامج أن الكائن c ينشئ كائنا t يتولي بدوره إنشاء كائن c وهكذا دواليك . يسمي هذا الأسلوب معاودة recursion ومن خطورتها أنه إذا لم يوضع شرط لإنهائها فقد يستمر البرنامج في العمل إلي مالا نهاية وقد يؤدي ذلم لانهيار النظام .
والشرط نقطة النهاية للقطع المستقيمة
يحدد الإحداثيان السيني والصادي لنقطة نهاية القطعة المستقيمة بالأمرين :
x=x+RAD*sin(theta);
y=y+RAD*cos(theta);
أي أن الإحداثي السيني لنقطة النهاية يزيد بمقدار RAD*sin(theta); والإحداثي الصادي بمقدار RAD*cos(theta); عن إحداثيات نقطة البداية (لا تنس أن نقطة نهاية كل قطعة هي نقطة بداية القطعة التالية) . ومن تطبيق قواعد الهندسة أن نقطة القيمتين المذكورتين هما ضلعي المثلث القائم الزاوية .
والبرنامج يمكن صياغته بكل سهولة بأسلوب البرمجة الإجرائية (دون اللجوء للكائنات) عن طريق دوارات متداخلة . ولكنك تري أن أسلوب الكائنات قد أضفي علي صياغة البرنامج وضوحا لا يستهان به .
ويمكنك بتغيير ثوابت البرنامج التغيير في شكل الزهرة المتولدة كأن تجعلها مثلا تتفرع إلي جهة اليمين بدلا من اليسار , أو في عدد الأفرع أو ألوانها .
تعليقات
إرسال تعليق