المتابعون للمدونة

الخميس، 1 مايو 2014

رسم الدوال


س: كيف يمكن رسم الدوال برمجيا؟
 

ج: المشروع الوجود على هذا الرابط:
يرسم أي دالة تريدها، وهو أحد مشاريع مرجع Mastering VB.NET الذي ترجمته عام 2003 باسم احتراف فيجوال بيزيك دوت نت.. وهذا هو شرح هذا المشروع في الكتاب المترجم:

افترض أنّ لديك المعادلة: ص = 2 س.
لرسم هذه الدالة، لا بدّ أن نوجد قيم ص المناظرة لكلّ قيم س (0، 1، 2، 3، ... إلخ).

ثم نقوم برسم النقاط الناتجة، حيث إنّ كلّ نقطة هي عبارة عن الإحداثيين (س، ص).. فمثلا في دالتنا هذه ستكون النقطة على الصيغة (س، 2س).. مثل: (0، 0) ، (1، 2) ، (2، 4) ... إلخ.

ولا بدّ أن نصل بين هذه النقاط بخطوط مستقيمة، حتّى يبدو المنحنى متصلا.

وهنا ستبرز هذه المشاكل:

1- إذا كان معدّل تغيّر الدالة كبيرا، فستجد أنّ النقاط متباعدة، بحيث ستكون الخطوط الواصلة بينها واضحة للعيان، وسيبدو المنحنى متكسرا غير ناعم.. لحلّ هذه المشكلة نحتاج لتغيير مقياس رسم المحور السيني (الأفقيّ).. وللقيام بذلك نطبّق المعادلة التالية:

مقياس الرسم الأفقي = عرض مربّع الصورة ÷ (أكبر س – أصغر س).

فمثلا: لو كنا سنرسم الدالة لقيم س بين -1 و 5، فسيكون:

مقياس الرسم = عرض مربّع الصورة ÷ (5 – [-1])

= عرض مربّع الصورة / 6.

2- إذا كانت قيم ص الناتجة من الدالة كبيرة، فإنّ كثيرا منها لن يظهر في منطقة الرسم.. ولحلّ هذه المشكلة، نحتاج لتغيير مقياس رسم المحور الصادي (الرأسيّ).

مقياس الرسم الرأسيّ = ارتفاع مربّع الصورة ÷ (أكبر ص – أصغر ص).

3- إذا كانت هناك قيم سالبة على المحور السينيّ أو الصاديّ، فإنّها لن تظهر في مساحة الرسم (لأنّ إحداثيات النموذج والأدوات تبدأ من (0، 0).. ولحلّ هذه المشكلة، يجب نقل الإحداثيّات، حتّى تصبح أكبر قيمة سالبة منطبقةً على الصفر.

وفي حالتنا هذه، سنقوم برسم النقط في كائن مسار الرسوم GraphicsPath، حيث سنصل بخطّ مستقيم، بين كلّ نقطة جديدة نضيفها إليه والنقطة السابقة لها، ثمّ سنرسم كائن المسار مرّة واحدة.. إنّ هذه الطريقة أسرع من رسم كلّ نقطة على حدة، كما أنّها تمكّنك من استخدام المتغيّر الذي يشير لكائن مسار الرسوم لإعادة رسم الدالة أكثر من مرّة بمقاييس رسم مختلفة.

ولكن كيف نقوم بهذه التحويلات؟

قلنا إنّ عمليات التحويل يتّم حفظها في مصفوفة التحويل.. لهذا سنستخدم هذه المصفوفة في حالتنا هذه، وسنجري عليها التحويلات مباشرة، وأوّل ما سنفعله هو تعريف متغيّر من مصفوفة التحويل:

World = New System.Drawing.Drawing2D.Matrix( )

 

ملحوظة:

هذه المصفوفة تتكوّن من 3 صفوف و 3 أعمدة، وهي تمتلك وسائل جاهزة لتدوير المصفوفة Rotate وعكسها Invert وضربها في مصفوفة أخرى Multiply.. ولقراءة محتويات مصفوفة التحويلات والحصول على مصفوفة عاديّة (Array 3x3) استخدم الخاصيّة Elements.

 

بعد ذلك سنغيّر مقياس الرسم أفقيّا ورأسيّا:

World.Scale(((PictureBox1.Width - 4) / (Xmax – Xmin)), _

- (PictureBox1.Height – 4) / (Ymax - Ymin))

لاحظ أنّنا طرحنا 4 من عرض وارتفاع مربّع الصورة، حتّى نزيل عرض الحوافّ ونترك هامشا بينه وبينها.. لاحظ أيضا أن أصغر وأكبر قيمتين على المحور الأفقي Xmin و Xmax هما اختياريتان، على حسب المدى الذي تريد رسم الدالّة فيه.. أمّا أصغر وأكبر قيمة على المحور الرأسي فلا بدّ من حسابهما أولا، سواء بالوسائل الرياضيّة التي تحسب القمّة العظمى والصغرى (سيحتاج هذا لإجراء عمليات تفاضل، وهذا خارج نطاقنا الآن)، أو بجملة تكراريّة تحسب كل قيم ص المناظرة لقيم س، بحيث تلتقط من بينها أصغر قيمة وأكبر قيمة.

بعد ذلك سنحرّك الإحداثيّات لتظهر كلّ النقط في منطقة الرسم:

World.Translate(-Xmin, -Ymax)

لاحظ أنّ الإحداثيّات في الكتب العلميّة والرياضيّة تفترض أنّ القيم على المحور الرأسي تزداد لأعلى وتقلّ لأسفل، لهذا فقد نقلنا الإحداثيّ الرأسيّ لأسفل بمقار أكبر قيمة له، بحيث تظهر هذه القيمة في أعلى الشاشة (ارتفاعها = 0 ).

الآن أصبحت مصفوفة التحويلات تحتوي على كل التحويلات المطلوبة، ولكي تنطبق هذه التحويلات على كائن مسار الرسوم، فإنّنا نستدعي الوسيلة Transform الخاصّة به، ونرسل هذه المصفوفة كمعامل لها، ثمّ بعد ذلك نرسمه:

GraphicPath1.Transform(World)

G.DrawPath(plotPen, plot1)

أعتقد الآن أن التساؤل الذي دار في ذهنك قد أجيب: إنّ الفارق بين استخدام مصفوفة التحويلات ووسائل التحويل الخاصّة بكائن الرسوم Graphics، هي أنّ مصفوفة التحويلات خاصّة فقط بالكائن الذي يستخدمها (كائن مسار الرسوم هنا)، أمّا وسائل التحويل الخاصّة بكائن الرسوم فهي عامّة لكل منطقة الرسم.

ولديك في مجلد برامج هذا الفصل، المشروع Plotting، وفيه سترى كيف يمكن رسم دالتين مختلفتين بواسطة المفاهيم التي شرحناها هنا.

أمّا لو أردت أن تسمح للمستخدم بتعريف الدالة التي يريد رسمها (بالصيغة الإنجليزيّة)، يمكنك استخدام الأداة Script ActiveX control.. ولإضافة هذه الأداة لبرنامجك، اضغط بزر الفأرة الأيمن على كلمة المراجع References في متصفّح المشاريع، ومن القائمة الموضعيّة اختر Add Reference، وفي مربّع الحوار الذي سيظهر اضغط الشريط COM.. ومن بين العناصر التي تملأ القائمة انقر مرتين على العنصر Microsoft Script Control 1.0.. بعد ذلك أغلق مربّع الحوار.

الآن يمكنك كتابة دالة تحسب لك قيمة الصيغة التي عرّفها المستخدم عند قيمة معيّنة لـ X، باستخدام إحدى نسخ VB المخصّصة لصفحات الإنترنت، وهي لغة VBScript، كالتالي:

Function FVal(X As Double, Formula As String) As Double

Dim S As New MSScriptControl.ScriptControl

S.Language = "VBScript"

Try

S.ExecuteStatement("X=" & X)

Return CDbl(S.Eval(Formula))

Catch exc As Exception

Throw New Exception("لا يمكن حساب الدالة عند X=" & X)

End Try

End Function

والآن جرّب استدعاء هذه الدالة كالتالي:

Dim F As String

F = "5 + 20 * Cos(X * 3) * Cos(X * 5) / Log(Abs(Sin(X)) / 10)"

MsgBox(FVal(3, F))

عند تنفيذ هذا الكود، ستظهر لك رسالة تحمل الرقم: 1.75091032730126.

جرّب وضع مربّع نصّ على نموذج، واكتب فيه صيغة الدالة (بالإنجليزيّة) ومربّع نصّ آخر تكتب فيه قيمة X التي تريد أن تعرف قيمة الدالة عندها، وزرا اكتب فيه ما يلي:

MsgBox(FVal(CDbl(TextBox2.Text), TextBox1.Text))

وفي ضوء هذا، حاول أن تطوّر مشروع رسم الدوال Plotting، ليسمح للمستخدم بتعريف صيغة الدالة التي يريدها.

وهو ما ستجده متحقّقا بالفعل في المشروع FunctionPlotting.

 

هناك تعليقان (2):

  1. عمل رائع ..
    لكن لدي سؤال ماذا تقصد بمربع الصورة ؟ ^_^ !

    ردحذف
  2. شكرا لك..
    مربع الصورة هي الترجمة المألوفة للأداة PictureBox .. من قرؤوا ذلك المرجع من أوله يألفون هذه الترجمة، وأذكر أنني نوهت في مقدمته إلى أنني سأضع الاسم الإنجليزي بجوار الترجمة العربية وأكرر ذلك عدة مرات إلى أن يألفه القارئ ثم أتوقف عنه وأستخدم المصطلح العربي فقط.. وطبعا الكود الموجود في المشروع يوضح هذا الأمر.
    تحياتي

    ردحذف

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.

صفحة الشاعر