المقالات العلمية

مقالات الشبكة العربية لمطوري الألعاب

رسم منحنيات Bézier باستخدام GDI+ و NET

هذه المقالة محمية ضمن الحقوق الفكرية لشركة In|Framez Technology Corp.، ومرخصة للعرض فقط ضمن الشبكة العربية لمطوري الألعاب مع الموافقة الصريحة من المؤلف وشركة In|Framez Technology Corp.. لا يسمح بإعادة نشر هذه المقالة أو تعديلها دون الرجوع للمؤلف. يمنع النسخ والاقتباس دون ذكر المصدر والموافقة من المؤلف.

الـ Curve هو باختصار منحني يصل بين نقطتين أو أكثر، إذا كان عدد النقط هو اثنان فقط كان الـ Curve على شكل خط مستقيم يصل بينهما.


و لنفرض أنه كان هناك ثلاث نقاط أ،ب،جـ فإن الـ Curve في هذه الحالة سيبدأ من النقطة "أ" ماراً بـ "ب" منتهياً بـ "جـ"، و طبعاً إذا كان هناك أكثر من ثلاث نقاط نتبع نفس الطريقة نبدأ من النقطة الأولى نمر بالثانية و الثالثة و هكذا ثم ننتهي بالأخيرة.


يجب أن لا تجعل كل النقاط على استقامة واحدة و إلا فستحصل على خط مستقيم يصل الأولى بالأخيرة، فقط!


كل خط يصل بين نقطتين في Curve يسمى قطعة أو جزء أي Segment، أي أن الـ Curve عبارة عن عدد من القطع Segments تتصل نهاية الأولى مع بداية الثانية و هلم جراً (أي تصل القطعة الأولى بين النقطة الأولى و الثانية، و تصل القطعة الثانية بين النقطة الثانية و الثالثة و هكذا)، أي الـ Curve المتعلق بنقطتين فقط يتألف من Segment واحد فقط هو خط مستقيم يصل بين النقطتين، نستنتج من ذلك (الهراء!) أن عدد القطع أو الـ Segments المتعلقة بـ Curve هو (عدد النقط - 1).

باستخدام تقنيات GDI Plus، و تحديداً باستخدام الطبقة System.Drawing.Graphics و بتحديد أكثر باستخدام الروتين DrawCurve (و له سبع حالات مختلفة) يمكن رسم Curve ببساطة شديدة، في العادة يأخذ هذا الروتين النقاط منك على شكل مصفوفة من النقاط من النوع System.Drawing.Point و لكنه لا يمانع في أخذ النوع System.Drawing.PointF، و يأخذ "قلم" من النوع System.Drawing.Pen.

إذا أردنا أن نكتب مثالاً سريعاً نعرف نسخة من الطبقة System.Drawing.Graphics هكذا:

Dim GraphicsObject As Graphics = Graphics.FromImage(PicBox.Image)

في الحالة السابقة (أي إذا استعملت أي PictureBox بدون إعداده مسبقاً) ستحصل فوراً على Exception من النوع ArgumentNullException و بنصه المعتاد:

Value cannot be null.
Parameter name: image

و لتلافي ذلك يوجد عدة طرق منها:

PicBox.Image = New Bitmap(PicBox.Width، PicBox.Height)

ثم نقوم باستدعاء الروتين Clear:

GraphicsObject.Clear(Color.White)

الآن فقط أصبحنا جاهزين لاستخدام الـ PicBox في الرسم.

نعرف مصفوفة من النقط المحببة PointF و لتكن باسم PointFsArray و لنحدد لها المجال 3 فقط أي:

Dim PointFsArray(2) As PointF

ثم نجرب رسم Curve يبداً من النقطة (100،10) و يمر بالنقطة (150،60) و ينتهي بالنقطة (200،10) و ذلك كما يلي:

PointFsArray(0) = New PointF(100، 10)
PointFsArray(1) = New PointF(150، 60)
PointFsArray(2) = New PointF(200، 10)

ثم نرسم Curve من هذه النقط:

GraphicsObject.DrawCurve(Pens.Black، PointFsArray)
PicBox.Refresh()

ملاحظة : قمنا بتحديث الـ PicBox حتى ترى نتيجة الكود أمام عينيك.

ملاحظة : أنصحك بتبديل القلم ذو اللون الأسود بقلم يحمل لونك المفضل 😉

يمكنني تلخيص الكود كله في روتين فرعي هو كالتالي (سيعمل الكود جيداً بمجرد اضافتك لـ PictureBox يحمل الاسم PicBox):

Private Sub IWantToDrawCurve()
    PicBox.Image = New Bitmap(PicBox.Width، PicBox.Height)
    Dim GraphicsObject As Graphics = Graphics.FromImage(PicBox.Image)
    GraphicsObject.Clear(Color.White)
    Dim PointFsArray(2) As PointF
    PointFsArray(0) = New PointF(100، 10)
    PointFsArray(1) = New PointF(150، 60)
    PointFsArray(2) = New PointF(200، 10)
    GraphicsObject.DrawCurve(Pens.Black، PointFsArray)
    PicBox.Refresh()
End Sub

يوجد خيار إضافي هو الشد أو Tension. تحدد هذه القيمة مدى "شد" الانحناء إلى النقاط الطرفية أكثر. إذا كان قيمته صفر كانت الـ Segments المتعلقة بالـ Curve مستقيمة تماماً.


لست مضطراً لرسم الـ Curve من النقطة الأولى و بالعدد الكامل من الـ Segments فلا تنسى الخيارين Offset و NumberOfSegments، و Offset هي رقم النقطة التي ستبدأ الرسم منها (النقطة الأولى رقمها صفر و ليس واحد)، و أما NumberOfSegments فهو عدد الـ Segments التي تريد رسمها.


إذا أردت أن تحدد الـ Offset بحيث تكون أكثر من الصفر و بنفس الوقت تريد الرسم حتى آخر نقطة، مثلاً النقاط "أ"، "ب" "جـ"، "د"، تريد البدء من النقطة "ب" و حتى آخر تقطة أي "د"، في هذه الحالة تحدد الـ Offset بحيث يكون 1 و أما الـ NumberOfSegments فهو يساوي (عدد النقاط - 1 - Offset)، أي (4 - 1 - 1 = 2).


لا تنسى بأنه يمكنك استعمال الـ Tension في كل الأحوال حتى عند تحديد الـ Offset و الـ NumberOfSegments.


إذا أردت رسم Curve مغلق فبنفس الطريقة استخدم الروتين DrawClosedCurve، لن يكون بامكانك استخدام الـ Offset و الـ NumberOfSegments لأنه شكل مغلق، إذا حددت الباراميتر Tension فيجب عليك تحديد FillMode أيضاً!


إذا أردت ملأ (Fill) للـ Curve فاستعمل الروتين FillClosedCurve، يجب عليك تحديد "فرشاة" من النوع System.Drawing.Brush و يمكنك تحديد الـ FillMode و الـ Tension.


الـ Bézier هو Curve يرسم بتحديد أربع نقاط حصراً، و ليس من الضرورة أن يلامس النقطتين الثانية و الثالثة، يرسم باستخدام الروتين DrawBezier الذي تحدد فيه إحداثيات النقاط الأربعة.


و يمكنك رسم سلسلة متواصلة من الـ Béziers يشترك كل واحد منها مع الذي يليه بنقطة، أي لرسم اثنان من Béziers (متصلين مع بعضهما) تحتاج لسبع نقاط فقط، نستنتج من ذلك شرط صحة عدد النقاط المستخدمة لرسم Bézier واحد أو عدد من الـ Béziers، هو (X % 3 = 1)، أي يجب أن يكون باقي القسمة على 3 مساوياً 1، مثلاً : 4،7،10.

الـ Methods المستخدمة في هذا المقال:

أضف تعليقاً

Loading