2 Nội dung nghiên cứu
2.29 Minh họa hoạt động của chương trình vẽ đồ thị trong không gian 3 chiều
Đồ thị không gian 3 chiều là đồ thị mà trong đó vị trí các đỉnh, các cạnh của nó được hiện thực trên mơi trường không gian 3 chiều. Không những vậy, với chương trình này, ta cịn có thể tương tác với đồ thị trong không gian 3 chiều thơng qua các tính năng như xem đồ thị trong khơng gian, chỉnh sửa lại đồ thị ...
Không gian vẽ
Trong đồ thị 3 chiều, không gian vẽ sẽ là không gian 3 chiều. Trong không gian này, một đỉnh sẽ nhận 3 giá trị x, y, z làm tọa độ của chúng. Gốc tọa độ của khơng gian nằm ở chính giữa màn hình. Vì lấy OpenGL làm thư viện đồ họa để xây dựng nên chương trinh này, do đó khơng gian 3 chiều ở đây tn theo quy tắc bàn tay phải, tức là chiều x hướng từ trái sang phải, chiều y từ dưới lên trên và chiều z từ trong ra ngồi.
Hình 2.30: Hệ trục tọa độ trong không gian 3 chiều của OpenGL7
Cài đặt cấu trúc đồ thị
Với không gian 3 chiều, đồ thị được tổ chức thành 3 thành phần chính sau:
• Đỉnh (vertex): đối tượng mơ tả thơng tin một đỉnh của đồ thị. Vì trong khơng gian 3 chiều, đối tượng này nhận 3 giá trị(x, y, z) - tương ứng với chiều rộng, chiều cao và chiều sâu - làm tọa độ của chúng. Ngoài ra, đối tượng này cịn chứa thơng tin tên của đỉnh đó. Đối tượng này được hiện thực thành một lớp, thành phần của lớp đó bao gồm 2 thành phần chính là tên và tọa độ của đỉnh này. 1 public class V e r t e x 3 D { 2 p r i v at e String name ; 3 p r i v at e P o i n t 3 f l o c a t i o n = null ; 4 5 // ... 6 }
Cùng với các thành phần đó, trong lớp Vertex3D cịn có các phương thức để thao tác với đồ thị nhưgetName()- lấy tên đỉnh, setName(...)- đặt tên dinh,getLocation()- lấy về thông tin tọa độ của đỉnh,setLocation(...) - đặt vị trí cho đỉnh đó
• Cạnh (edge): đối tượng mơ tả thơng tin của một cạnh trong đồ thị. Thông tin của cạnh gồm có tên cạnh, 2 đỉnh đầu cuối của cạnh này ...
Đối tượng cạnh cũng được hiện thực thành một lớp với tên gọi Edge3D, trong lớp Edge3D chứa những thông tin cần thiết cho cạnh được tạo.
1 public class Edge3D {
2 p r i v at e V e r t e x 3 D from , to ; 3 p r i v at e String name ; 4
5 // ..
được đặt theo thứ tự mà chúng được thêm vào, như cạnh thứ nhất được thêm vào sẽ làa, tương
tự cho các cạnh ở sau.
• Đồ thị (graph): đối tượng mơ tả thơng tin của một đồ thị. Trong đồ thị bao gồm các tập chứa thông tin các đỉnh, các cạnh .. Đồ thị là thành phần quan trọng nhất trong chương trình, với đồ thị trong không gian 3 chiều, chúng tôi tổ chức như sau
1 public class G r a p h 3 D {
2 public ArrayList < Vertex3D > V = new ArrayList < Vertex3D >() ; 3 public ArrayList < Edge3D > E = new ArrayList < Edge3D >() ; 4
5 // ..
6 }
Đồ thị được cài đặt ở đây bao gồm một tập các đỉnh và các cạnh nối với đỉnh đó. Đồ thị này cho phép thêm hay bớt các đỉnh, các cạnh thông qua các hàmget/set được cài đặt sẵn. Đồng thời, đồ thị có khả năng hiển thị trên khơng gian 3 chiều thông qua phương thứcdraw(...) - phương thức này nhận vào một "không gian vẽ" và hiển thị lên trên khơng gian đó. Đồ thị được hiển thị qua các bước sau:
1- Xây dựng tọa độ của các đỉnh của đồ thị ứng với không gian vẽ. Ở bước này đồ thị sẽ sinh ra các giá trị ngẫu nhiên trong khơng gian 3 chiều, và đặt tọa độ đó tương ứng cho mỗi đỉnh của chúng. Tuy nhiên, nếu chỉ sinh tọa độ một cách ngẫu nhiên như vậy, sẽ có những lúc các đỉnh của đồ thị sẽ bị chồng chéo lên nhau. Chính vì thế, chúng tơi giới hạn khoảng cách giữa các đỉnh với nhau phải lớn hơn 0.5 (trong không gian 3 chiều, giới hạn vẽ được chúng tôi quy định từ -1.0 - 1.0). Việc tính tốn sắp xếp các đỉnh như thế được hiện thực qua thuật toán sau
1 b o o l e a n stop = false ; 2 while (! stop ) 3 { 4 p = new P o i n t 3 f ( r . n e x t F l o a t () , r . n e x t F l o a t () , r . n e x t F l o a t () ) ; 5 6 int j ; 7 8 for ( j = i - 1; j >= 0; j - -) 9 { 10 if ( G e o m e t r y 3 D . d i s t a n c e ( V . get ( j ) . g e t L o c a t i o n () , p ) < 0.5) 11 break ; 12 } 13
14 if ( j < 0) // chay het vong lap - thoa toan bo dk
15 stop = true ;
16 }
Giải thuật là tính tốn khoảng cách của các đỉnh trong không gian 3 chiều, nếu tọa độ của đỉnh đó thỏa mãn điều kiện là nó cách các đỉnh khác một khoảng ít nhất là 0.5 thì sẽ chọn; nếu khơng thỏa điều kiện thì sẽ sinh ra tọa độ khác để kiểm tra. Khi tất cả các đỉnh đều thỏa mãn điều kiện được đặt ra, thì tiếp tục tiến hành bước vẽ các cạnh của đồ thị.
2- Việc vẽ các cạnh của đồ thị được thực hiện qua đoạn mã sau:
1 P o i n t 3 f from , to ;
2 from = e . g e t F r o m () . g e t L o c a t i o n () ; 3 to = e . getTo () . g e t L o c a t i o n () ; 4
3- Cuối cùng, đồ thị sẽ vẽ các đỉnh và tên đỉnh tương ứng lên trên vùng không gian vẽ.
Đồ thị không thể được vẽ nếu thiếu các lớp hỗ trợ việc vẽ đồ thị, trong đề tài này chúng tôi tách biệt ra các lớp hỗ trợ của đồ thị 2 chiều với đồ thị 3 chiều.
Các lớp hỗ trợ
Các lớp hỗ trợ được chúng tôi xây dựng với chức năng hỗ trợ việc vẽ đồ thị trong không gian 3 chiều. Các lớp hỗ trợ này được chia làm 2 phần chính gồm có: các lớp hỗ trợ về đồ họa - các lớp này có chức năng làm mơi trường để cho đồ thị có thể tương tác được và các lớp hỗ trợ việc tính tốn - ví dụ như tính khoảng cách, tính góc xoay. Ngồi ra cịn một số lớp hỗ trợ cho việc tổ chức chương trình như lớp
Point3f là hiện thực của một điểm trong không gian 3 chiều, hay lớp Color3f là hiện thực cho một màu khi sử dụng với môi trường OpenGL; cũng như các lớp hộp thoại hỗ trợ nhập dữ liệu, chỉnh sửa dữ liệu cho đồ thị. Các lớp hỗ trợ gồm có lớp sau:
• Các lớp hỗ trợ về đồ họa:GLCanvas3D, GLGraphics3D
• Các lớp hỗ trợ về tốn học:Geometry3D
• Các lớp khác:EdgeDlg3D, Point3f, Color3f
Các lớp hỗ trợ này đóng một vai trị khơng thể thiếu trong việc hiện thực đồ thị trong không gian 3 chiều. Một số điểm nhấn trong các lớp hỗ trợ mà chúng tôi đã cài đặt như sau:
GLCanvas3D8
Đây là lớp hiện thực hóa mơi trường khơng gian 3 chiều. Chức năng của lớp này tương tự như một nơi để ta có thể vẽ lên. Việc vẽ lên trênGLCanvas3D được thông qua một đối tượng làGL, đối tượng này
có thể được hiểu như là hiện thực của thư viện đồ họaOpenGLtrên mơi trường Java. Để có thể sử dụng được lớp này, ta cần phải cài đặt các thành phần chính sau: Thành phần đầu tiên là phương thức thiết lập của đối tượng này, trong phương thức thiết lập này, ta cài đặt các thuộc tính cho mơi trường vẽ, ví dụ như khử răng cưa, làm nét vẽ mượt hơn. Đoạn mã cài đặt như sau:
1 public G L C a n v a s 3 D ( G L C a p a b i l i t i e s I m m u t a b l e c a p a b i l i t i e s I m m u t a b l e , M a i n A p p l e t p a r e n t A p p l e t ) 2 { 3 super ( c a p a b i l i t i e s I m m u t a b l e ) ; 4 this . a d d G L E v e n t L i s t e n e r ( this ) ; 5 6 t h e A p p l e t = p a r e n t A p p l e t ; 7 8 // gan m o u s e l i s t e n e r va m o u s e m o t i o n l i s t e n e r 9 a d d M o u s e L i s t e n e r ( new M o u s e A d a p t e r () { 10 @ O v e r r i d e 11 public void m o u s e P r e s s e d ( M o u s e E v e n t e ) { 12 // TODO Auto - g e n e r a t e d m e t h o d stub
13 super . m o u s e P r e s s e d ( e ) ; 14 15 // S y s t e m . out . p r i n t l n (" M o u s e p r e s s e d ") ; 16 17 p r e P o i n t = e . g e t P o i n t () ; 18 19 // gan m o u s e m o t i o n
25 // TODO Auto - g e n e r a t e d m e t h o d stub 26 super . m o u s e R e l e a s e d ( e ) ; 27 28 r e m o v e M o u s e M o t i o n L i s t e n e r ( m o t i o n H a n d l e ) ; 29 } 30 }) ; 31 32 }
Ngồi việc cài đặt các thc tính cho mơi trường vẽ, tương tự như một thành phần của Java, ta có thể thêm vào cácListener để xử lý các sự kiện trong môi trường không gian 3 chiều. Ở đoạn mã trên, môi trường khơng gian 3 chiều được cài đặt để có thể nhận được các sự kiện chuột khác nhau, và tùy theo sự kiện chuột xảy ra như thê nào mà xử lý tương ứng với sự kiện đó. Để lớpGLCanvas3D có thể hoạt động, ta cần phải kế thừa các phương thức đã được xây dựng sẵn:
• Phương thứcinit(...): phương thức này được tự động gọi khi đối tượng GLCanvas3D được tạo, nhiệm vụ của phương thức này nhằm khởi tạo mơi trường, ngồi ra nếu cần cài đặt thêm những thuộc tính khác thì ta sẽ thêm vào trong phương thức này. Đặc điểm của phương thức này chỉ được chạy một lần duy nhất khi khởi tạo đối tượng.
• Phương thứcreshape(...): phương thức này được gọi tự động mỗi khi có sự thay đổi của khơng
gian vẽ. Ví dụ như thay đổi chiều rộng, chiều cao của vùng vẽ, thay đổi các thuộc tính ...
• Phương thức display(...): phương thức được chương trình tự động gọi, trong phương thức này,
người dùng cần định nghĩa sẽ vẽ gì trong chương trình. Cài đặt lớpGLCanvas3D như sau:
1 public class G L C a n v a s 3 D e x t e n d s G L C a n v a s i m p l e m e n t s G L E v e n t L i s t e n e r { 2 3 p r i v a te G L G r a p h i c s 3 D g r a p h i c s 3 D ; 4 5 // ... 6 7 public G L C a n v a s 3 D ( M a i n A p p l e t p a r e n t A p p l e t ) { 8 // ... 9 } 10 11 public G L C a n v a s 3 D ( G L C a p a b i l i t i e s I m m u t a b l e c a p a b i l i t i e s I m m u t a b l e , M a i n A p p l e t p a r e n t A p p l e t ) 12 { 13 // ... 14 } 15 16 @ O v e r r i d e
17 public void init ( G L A u t o D r a w a b l e d r a w a b l e ) {
18 GL2 gl = d r a w a b l e . getGL () . getGL2 () ; // lay ve O p e n G L c o n t e x t
19
20 g r a p h i c s 3 D = new G L G r a p h i c s 3 D ( gl ) ;
21 }
22
23 @ O v e r r i d e
24 public void r e s h a p e ( G L A u t o D r a w a b l e drawable , int x , int y , int width , int height ) { 25
26 // dieu c h i n h lai kich t h u o c vung ve
27 g r a p h i c s 3 D . s e t S i z e ( width , height ) ;
28 }
29
31 public void d i s p l a y ( G L A u t o D r a w a b l e d r a w a b l e ) { 32 // ... 33 34 // ve do thi 35 if (! t h e A p p l e t . g r a p h 3 d . i s E m p t y () ) 36 t h e A p p l e t . g r a p h 3 d . draw ( graphics3D , g e t C a m e r a () ) ; 37 } 38 39 @ O v e r r i d e
40 public void d i s p o s e ( G L A u t o D r a w a b l e arg0 ) { 41 // TODO Auto - g e n e r a t e d m e t h o d stub
42 // ...
43 }
44 }
GLGraphics3D
Mục đích của chương trình là sử dụngOpenGLđể thực hiện các thao tác vẽ hình trong khơng gian. Tuy nhiên,OpenGLchỉ bao gồm các hàm API, cùng với một số phương thức vẽ các hình cơ bản như đưởng thẳng, hình tam giác ... khơng đủ để đáp ứng u cầu của chương trình. Vì thế, chúng tơi xây dựng lớp
GLGraphics3D với mục đích đơn giản hóa việc vẽ hình. LớpGLGraphics3D sẽ tương tác trực tiếp với các hàm thư việnOpenGLđể thực thi các thao tác vẽ. Ta không cần quan tâm đối tượng này làm việc đó như thể nào mà chỉ cần sử dụng các phương thức được cung cấp sẵn ở dạng đơn giản, trực quan. Trong lớp này, chúng tôi đã cài đặt một số phương thức cơ bản giúp cho việc thao tác với thư viện đồ họa bên dưới dễ dàng hơn, ví dụ như phương thức vẽ đường thẳng, vẽ chuỗi ... Với lớp hỗ trợ đồ họa này, chúng tơi cài đặt gồm có 3 phần chính: phần khởi tạo, các phương thức vẽ và các phương thức tùy chỉnh thuộc tính của lớp đồ họa.
• Phần khởi tạo: trong phần này, ta cần cung cấp các thông số choGLGraphics3D, để đối tượng này
có thể hoạt động được. Thuộc tính củaGLGraphics3D gồm các thông số sau:
1 GL2 m_gl ;
2 GLU m_glu ;
3 GLUT m_glut ;
4 T e x t R e n d e r e r m_tr ; 5
6 C o l o r3 f m _ d r a w C o l o r = new C o l o r 3 f ( Color . BLACK ) ; 7 C o l o r3 f m _ f i l l C o l o r = new C o l o r 3 f ( Color . WHITE ) ;
Một vài thuộc tính quan trọng của lớpGLGraphics3D có tác dụng như sau:
- GL2: Thành phần chính của lớp GLGraphics3D, tất cả các phương thức vẽ đều thông qua
thành phần này để có thể vẽ được trên vùng cần vẽ. - TextRenderer: Hỗ trợ việc vẽ chữ trong không gian 3D.
Ngồi ra, ta cịn cần khởi tạo thông tin của vùng cần vẽ thông qua phương thứcsetSize(...). Trong
không gian 3 chiều, các thơng tin để khởi tạo vùng cần vẽ ngồi chiều rộng và chiều cao, ta cần phải khai báo phép chiếu ta sử dụng. Nhưng để đơn giản cho việc sử dụng, các thông tin này đã được cài đặt sẵn. Do vậy, ta chỉ cần đưa chiều dài và chiều rộng của vùng cần vẽ. Minh họa cài đặt như sau:
7 m_glu . g l u P e r s p e c t i v e (45.0 , aspect , 0.0 , 100.0) ; // cai dat goc nhin , ty le va do xa gan
8
9 m_gl . g l M a t r i x M o d e ( GL2 . G L _ M O D E L V I E W ) ;
• Các phương thức vẽ: sau khi cài đặt việc khởi tạo đối tượng GLGraphics3D, để có thể cung cấp
khả năng vẽ hình choGLGraphics3D, ta cần định nghĩa các phương thức vẽ tương ứng trong lớp
này. Một số phương thức vẽ cơ bản có thể là:drawLine(...),drawString(...)... Các phương thức vẽ này giúp cho đơn giản hóa việc vẽ hình. Ta chỉ cần gọi phương thức vẽ tương ứng, đối tượng sẽ xử lý dựa theo thông tin đã được cung cấp sẵn chứ không cần phải thao tác trực tiếp với các hàm API củaOpenGL. Ví dụ như hàm vẽ một đường thẳng dưới đây:
1 public void d r a w L i n e ( P o i n t 3 f A , P o i n t 3 f B ) 2 { 3 m_gl . g l P u s h M a t r i x () ; 4 5 // dat mau d u o n g t h a n g 6 m_gl . g l C o l o r 3 f ( m _ d r a w C o l o r .R , m _ d r a w C o l o r .G , m _ d r a w C o l o r . B ) ; 7 8 m_gl . g l Be g i n ( GL2 . G L _ L I N E S ) ; 9 m_gl . g l V e r t e x 3 f ( A .x , A .y , A . z ) ; 10 m_gl . g l V e r t e x 3 f ( B .x , B .y , B . z ) ; 11 m_gl . glEnd () ; 12 13 m_gl . g l P o p M a t r i x () ; 14 }
• Các phương thức khác: ngồi các phương thức khởi tạo và phương thức vẽ, lớpGLGraphics3D còn cung cấp một loạt các phương thức giúp ta tùy chỉnh thuộc tính vẽ, tùy chỉnh khơng gian vẽ như xoay hình, đổi màu vẽ, dịch chuyển hình...
Geometry3D
Trong quá trình xây dựng phần vẽ đồ thị trong khơng gian 3 chiều, chương trình có sử dụng một số các tính tốn trong khơng gian 3 chiều. Vì vậy, chúng tơi xây dựng lớp Geometry3D nhằm mục đích cung cấp một thư viện gồm các hàm tính tốn khác nhau. Nhưng do giới hạn của chương trình, do đó chúng tơi chỉ cài đặt những hàm nào mà thật sự cần sử dụng để tránh sự dư thừa không cần thiết. Một trong số đó là hàm tính khoảng cách trong khơng gian 3 chiều. Hàm này nhận vào tọa độ của 2 điểm bất kì trong khơng gian 3 chiều và trả về khoảng cách giữa chúng. Giả sử tọa độ của 2 điểm trong không gian lần lượt làx1, y1, z1 vàx2, y2, z2, công thức của hàm này như sau:
d=p
(x2−x1)2+ (y2−y1)2+ (z2−z1)2
Edge3D
Để có thể tạo ra giao diện hộp thoại giao tiếp với dùng nhận dữ liệu cho đồ thị trong môi trường không gian 3 chiều, chúng tôi xây dựng một lớp hộp thoại mới với chức năng cho phép nhập các thông tin về đồ thị không gian 3 chiều là số cạnh và quan hệ các cạnh đó. Ngồi ra, lớp hộp thoại này cịn có chức năng chỉnh sửa lại cấu trúc đồ thị dựa vào thông tin đồ thị được nhận vào. Trong hộp thoại này, các thuộc tính của nó bao gồm
1 p r i v a te final JPanel c o n t e n t P a n e l = new JPanel () ; 2
3 // mang chua cac C h e c k Box
4 p r i v a te ArrayList < JCheckBox > m _ c h e c k B o x e s = new ArrayList < JCheckBox >() ; 5
6 p r i v a te G r a p h 3 D m _ g r a p h ; 7 p r i v a te b o o l e a n o k P r e s s e d ;
8 p r i v a te b o o l e a n m o d i f y M o d e = false ;
Đối tượng đồ thị này hoạt động như sau:
- Đầu tiên, đối tượng hộp thoại này nhận vào thông tin của đồ thị bao gồm cấu trúc của đồ thị và số đỉnh của đồ thị đó. Dựa vào số lượng đỉnh được nhận vào, hộp thoại sẽ tự động xây dựng quan hệ giữa các đỉnh dựa trên thông tin số lượng đỉnh cho trước. Ví dụ như ta có đỉnh A, thì chương