สารบัญ
เมื่อวานแนะนำไฟล์เฮดเดอร์ซึ่งเป็นส่วนนิยามคลาสไปแล้ว. วันนี้ขอแนะไฟล์ dragonfractal.cpp ซึ่งเป็นส่วนรหัสสร้างอ็อบเจ็ค.
ไฟล์ dragonfractal.cpp
ขอแทรกคำอธิบายไปในระหว่างไฟล์เลยแล้วกันครับ.
1 #include "dragonfractal.h"
2 #include <iostream>
3 #include <qpainter.h>
4 #include <qwmatrix.h>
5 #include <qpoint.h>
6 using namespace std;
ช่วงแรกเป็นการ include ไฟล์เฮดเดอร์ต่างๆที่จำเป็นสำหรับการคอมไพล์. iostream ใช้กับฟังก์ชัน printDirection() สำหรับแสดงผลทางเทอร์มินอล.
qpainter.h เป็นไฟล์เฮดเดอร์ของคลาส
QPainter ใช้สำหรับวาดเส้น, รูปต่่างๆในวิดเจ็ด. ไฟล์เฮดเดอร์
qwmatrix.h เป็นไฟล์เฮดเดอร์ของคลาส
QWMatrix ใช้สำหรับเปลี่ยนระบบจุดพิกัดย้ายจุดเริ่มต้น, หมุน ฯลฯ.
9 void DragonFractal::init(int order)
10 {
11 resize(400,400);
12 setBackgroundColor(Qt::white);
13 setBeginX( width() / 2);
14 setBeginY( height() / 3 );
15 setBlockWidth(10);
16 setOrder( order);
17 }
ฟังก์ชัน init() เป็นฟังก์ชันสำหรับตั้งค่าเริ่มต้นต่างๆเวลาสร้างอ็อบเจ็ค. ฟังก์ชันนี้จะเรียกใช้จากคอนสตรักเตอร์อีกที. บรรทัดที่ 11 และ 12 เป็นการใช้ฟังก์ชันที่สืบทอดมาจากคลาส
QWidget ได้แก่
resize() เปลียนขนาดวิดเจ็ดให้มีขนาด 400x400 พิกเซล. ต่อจากนั้นใช้ setBackgroundColor() เซ็ตสีพื้นฉากหลังให้เป็นสีขาว. พึ่งเห็นว่าฟังก์ชันนี้ควรเลิกใช้แล้ว (obsolete) ให้ใช้ฟังก์ชัน
setPaletteBackgroundColor แทน. ใน Qt มีการ
นิยามสีต่างๆที่ใช้บ่อยไว้ให้แล้วเช่นสีแดง
Qt::red, สีดำ
Qt::black เป็นต้น.
บรรทัดที่ 13 และ 14 เป็นการตั้งจุดเริ่มต้นของแฟรกทอลโดยคำนวณจากความกว้างและสูงของวิดเจ็ด. ฟังก์ชัน setBlockWidth ใช้ตั้งความยาวของเส้นที่วาดแต่ละครั้ง. ส่วน setOrder เซ็ตจำนวนทบของกระดาษที่พับ.
20 DragonFractal::DragonFractal( QWidget *parent, char *name, int order)
21 : QWidget( parent, name)
22 {
23 init(order);
24 }
25
26 DragonFractal::DragonFractal( int order)
27 : QWidget( 0, 0)
28 {
29 init(order);
30 }
บรรทัดที่ 20 - 30 เป็นคอนสตรักเตอร์สร้างไว้สองแบบ. โดยปรกติคอนสตรักเตอร์ที่เป็นวิดเจ็ดจะรับอาร์กิวเมนต์สองตัวได้แก่พอนท์เตอร์ชี้ไปที่วิดเจ็ดพ่อแม่และชื่อ. ในที่นี้เพิ่มอาร์กิวเมนต์อีกตัวคือ order. จะเห็นว่าในคอนสตรักเตอร์มีการเรียกคอนสตรักเตอร์ของคลาสที่สืบทอดมาด้วย.
33 void DragonFractal::setBeginX( int x)
34 {
35 bx = x;
36 }
37 void DragonFractal::setBeginY( int y)
38 {
39 by = y;
40 }
41 void DragonFractal::setBlockWidth( int w)
42 {
43 bw = w;
44 }
บรรทัดที่ 33 - 44 เป็นฟังก์ชันสำหรับตั้งค่าคุณสมบัติต่างๆของอ็อบเจ็คท์. ไม่ได้ทำอะไรมากแค่เอาค่าที่ได้รับก็อปปี้ใส่ในตัวแปรที่เป็นเมมเบอร์ของอ็อบเจ็คท์.
45 void DragonFractal::setOrder( int order)
46 {
47 o = order;
48 int c; // for flip-flop left-right
49 for( int i=1; i<= o; i++){
50 if( i == 1){
51 // true : left, false : right
52 direction.append(true);
53 } else {
54 QValueList::iterator it = direction.begin();
55 c = 0;
56 while( it != direction.end())
57 {
58 if( c % 2 == 0 )
59 {
60 direction.insert(it, true);
61 } else {
62 direction.insert(it, false);
63 }
64 ++c;
65 ++it;
66 }
67 direction.append(false);
68 }
69 }
70 }
ฟังก์ชัน setOrder เป็นฟังก์ชันสำหรับใช้สร้างเส้นทางสำหรับวาดแฟรกทอล. ตัวแปร c ใช้สำหรับตรวจสอบว่าตอนไหนควรจะใส่ค่า L (true) หรือ R (true) ในลิงค์ลิส direction. ถ้าจำนวน order เป็น 1 จะมีการเลี้ยวซ้ายอย่างเดียว. ถ้า order มากกว่า 1 จะเข้าวงวน while ใช้ iterator ของ QValueList เป็นตัวช่วยวน. ถ้าค่าตัวแปร c เป็นเลขคู่ (c%2 == 0) ก็จะแทรก L (true) หน้าลิสที่กำลังจัดการอยู่. ถ้าค่าตัวแปร c เป็นเลขคี่ก็จะแทรก R (false). พอออกจากวงวน while ก็เพิ่ม R (false) ตามหนึ่งตัว. ถ้า order เท่ากับ 3 จะได้ค่าของตัวแปร direction เป็นลิสของ LLRLLRR.
L i=1
L R i=2
L R L R i=3
ผลสุดท้าย LLRLLRR.
71 void DragonFractal::setStartPoint( int x, int y)
72 {
73 setBeginX(x);
74 setBeginY(y);
75 }
76 void DragonFractal::setStartPoint( QPoint& p)
77 {
78 setBeginX(p.x());
79 setBeginY(p.y());
80 }
บรรทัดที่ 71 - 80 เป็นฟังก์ชันสำหรับตั้งจุดเริ่มต้น. ไม่มีอะไรมาเพียงแค่เรียกฟังก์ชัน setBeginX, setBeginY ต่ออีกที.
QPoint เป็นคลาสสำหรับจุดพิกัด.
84 void DragonFractal::printDirection()
85 {
86 QValueList::iterator it = direction.begin();
87 while( it != direction.end())
88 {
89 if( *it )
90 cout << "L";
91 else
92 cout << "R";
93 ++it;
94 }
95 cout << endl;
96 }
ฟังก์ชัน printDirection ใช้แสดงเส้นทางการวาดแฟรกทอลทางเทอร์มินอล. เนื่องจากค่าที่อยู่ในลิส direction เป็น bool เวลาแสดงผลเปลี่ยน true ให้แสดงด้วยอักษร L และเปลี่ยน false ให้แสดงด้วย R.
98 void DragonFractal::drawFractal( QPainter &painter, QWMatrix &matrix)
99 {
100 painter.setPen(QPen(black));
101 matrix.translate( bx, by); // goto begin point
ฟังก์ชัน drawFractal ใช้วาดแฟรกทอลบนวิดเจ็ด. จะรับ reference ของอ็อบเจ็คท์ QPainter กับ QWMatrix มา. painter.
setPen เป็นฟังก์ชันของ QPainter ใช้ตั้งค่าปากกาให้มีสีดำ. matrix.
translate เปลี่ยนจุดพิกัดเริ่มต้น 0,0 ไปที่จุด bx, by.
104 QValueList::iterator it = direction.begin();
105 while( it != direction.end())
106 {
107 painter.setWorldMatrix(matrix);
108 painter.drawLine(0,0,0, bw);
109 matrix.translate(0, bw);
110 if( *it ) { // if we have to turn left
111 matrix.rotate(-90);
112 } else { // Right
113 matrix.rotate(90);
114 }
115 ++it;
116 }
117 painter.setWorldMatrix(matrix);
118 painter.drawLine(0,0,0, bw);
119 }
จากนั้นในบรรทัดที่ 104 -105 อ่านค่าเส้นทางที่อยู่ในตัวแปร direction. เวลาเปลี่ยนจุดพิกัดด้วย matrix.translate แล้วยังไม่มีผลทันที, ต้องเซ็ตให้ painter ใช้ matrix นั้นด้วยฟังก์ชัน
setWorldMatrix แล้ววาดเส้นตรงขนาด bw ไปข้างหน้า (ตรงไปทางแกน y).
121 void DragonFractal::paintEvent(QPaintEvent *)
122 {
123 QPainter painter(this);
124 QWMatrix matrix;
125 drawFractal( painter, matrix);
126 }
ฟังก์ชัน
paintEvent เป็นฟังก์ชันที่ต้องเขียนเพื่อวาดรูป. ในฟังก์ชันี้จะเป็นที่สร้าง QPainter และ QWMatrix แล้วส่งให้ฟังก์ชัน drawFractal วาดต่อไป.
128 void DragonFractal::resizeEvent( QResizeEvent *e)
129 {
130 setStartPoint( (int)(bx * e->size().width() / e->oldSize().width()),
131 (int)(by * e->size().height() / e->oldSize().height()));
132 }
ฟังก์ชันสุดท้ายคือ
resizeEvent เป็นฟังก์ชันจัดการอีเวนท์เหมือนกับ paintEvent. ฟังก์ชันนี้จะรับพอนท์เตอร์ของ
QResizeEvent เป็นอาร์กิวเมนต์. ทำให้เรารู้ข้อมูลเกี่ยวกับขนาดของวิดเจ็ดใหม่ด้วยฟังก์ชัน
size() และรับรู้ขนาดของวิดเจ็ดก่อนเปลี่ยนขนาดจากฟังก์ชัน
oldSize(). แล้วคำนวณจุดเริ่มต้นของแฟรกทอลใหม่. เนื่องจากมีการเปลี่ยนขนาดของวิดเจ็ด, ทำให้เกิดอีเวนท์ paintEvent โดยอัตโนมัติ, วิดเจ็ดจะวาดตัวเองใหม่.
ไฟล์ main.cpp
ไฟล์ main.cpp เป็นไฟล์ที่สร้างแอพพลิเคชันเหมือนโปรแกรม qt อื่นๆทั่วไป. สร้างอ็อบเจ็คท์ QApplication แล้วเซ็ตวิดเจ็ดหลัก, เรียกฟังก์ชัน exec() ทำงาน.
1 #include
2 #include
3 #include "dragonfractal.h"
4
5 int main( int argc, char *argv[])
6 {
7 QApplication app( argc, argv);
8 // DragonFractal *a = new DragonFractal(0,0,QString(argv[1]).toInt());
9 DragonFractal *a = new DragonFractal(QString(argv[1]).toInt());
10 // a->printDirection();
11 a->show();
12 app.setMainWidget( a);
13 app.exec();
14 }
ต่อจากนี้
รหัสที่เขียนนี้ไม่สมบูรณ์เท่าไหร่. ยังมีที่ต้องแก้ไขอีกเยอะ. ความหน้าจะเพิ่มความสามารถใช้เมาส์ลากรูปแฟรกทอลไปในตำแหน่งที่ต้องการ. และจะได้แนะนำคำสั่ง
diff และ
patch ไปในตัว.